Partager via


将用户帐户从 Windows 声明迁移到 SAML 声明

将用户帐户从 Windows 声明迁移到 SAML 声明

在最近的工作中,我一直忙着关注那些喜欢一开始作为 Windows 声明用户又转而使用 SAML 声明的那些人。 听起来没什么问题,但是问题在于我们并没有现成的方法来将帐户从 Windows 声明迁移到 SAML 声明。 不过有个好消息,就是在 2010 年 8 月 CU 的挂接中添加了 SharePoint 产品组,让您可以在 MigrateUsers 方法中运行您自己的自定义代码。 我们将很快推出关于 API 的全部文档以及由 Bryan P. 和 Raju S. 提供的一些出色的代码示例,我的示例也是在这些代码示例基础上做出的。 他们的新 API(实际上是一个接口 – IMigrateUserCallback)文档工作做得极为出色,因此这里我就不必详述了。 一旦有了最近发布的相关信息的链接,我会立即补充到此博客文章中。

还是像以往那样,先展示我的自定义迁移类的代码,然后讨论一下我认为有意思的部分。

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Diagnostics;

using System.Security;

using System.Security.Principal;

//add references to Microsoft.SharePoint and Microsoft.IdentityModel for these

using Microsoft.SharePoint;

using Microsoft.SharePoint.Administration;

using Microsoft.SharePoint.Administration.Claims;

using Microsoft.IdentityModel.Claims;

namespace MigrateUserSample

{

   public class MigrateTest : IMigrateUserCallback

   {

       public string SPTrustedIdentityTokenIssuerName { get; set; }

       public MigrateTest(string TrustedIdentityTokenIssuerName)

       {

          SPTrustedIdentityTokenIssuerName = TrustedIdentityTokenIssuerName;

       }

       public string ConvertFromOldUser(string oldUser,

              SPWebApplication.AuthenticationMethod authType, bool isGroup)

       {

          string value = string.Empty;

          try

          {

              switch (authType)

              {

                 case SPWebApplication.AuthenticationMethod.Windows:

                     //code for converting from classic Windows would be here

                     Debug.WriteLine(oldUser);

                     break;

                 case SPWebApplication.AuthenticationMethod.Claims:

                     //this is the only scenario this sample will cover

                     //migrating from Windows claims to SAML claims

                     Debug.WriteLine(oldUser);

                     //get the claim provider manager

                     SPClaimProviderManager cpm = SPClaimProviderManager.Local;

                     //create a claim from the identifier so we can see if the

                     //original issuer came from Windows

                     SPClaim idClaim = cpm.ConvertIdentifierToClaim(oldUser,

                           SPIdentifierTypes.EncodedClaim);

                     //this is a Windows claims user, and we are going to

                     //convert to a SAML claims user

                     if (idClaim.OriginalIssuer == "Windows")

                     {

                        //windows claims users will be in the format domain\user;

                        //windows claims groups will be in the SID format

                        if (idClaim.Value.Contains("\\"))

                        {

                           //migrating a user

                           //you will want to check the identity of the user here

                           //there may be some Windows claims accounts you don't want to

                           //convert yet, and there will also be service accounts that

                           //are passed in that you may not want to convert either;

                           //ideally you would just read from a data source to determine

                           //which users you should convert, and then check the identity

                           //here to see if it's one of the users that should be

                           //converted

                           //in this case, I'm only converting one user - darrins

                           if (idClaim.Value == "contoso\\darrins")

                           {

                               //I’m getting an identity claim here, grabbing the

                               //part after the "domain\", and appending the email

                               //suffix to it, so it becomes darrins@contoso.com

                               SPClaim migratedUserClaim =

                                         SPClaimProviderManager.CreateUserClaim(

                                         idClaim.Value.Split('\\')[1] + "@contoso.com",

                                         SPOriginalIssuerType.TrustedProvider,

                                         SPTrustedIdentityTokenIssuerName);

                               //get the encoded value of what the new identity

                               //claim will be

                               value = migratedUserClaim.ToEncodedString();

                           }

                        }

                        else

                        {

                           //migrating a group

                           //get the plain name of the group

                           SecurityIdentifier sid =

                               new SecurityIdentifier(idClaim.Value);

                           NTAccount groupAccount =

                               (NTAccount)sid.Translate(typeof(NTAccount));

                           string groupName = groupAccount.ToString();

                           //only interested in migrating the Portal People group

                           if (groupName.ToLower() == "contoso\\portal people")

                           {

                               //create a new role claim

                               SPClaim migratedGroupClaim =

                                  new SPClaim("https://schemas.microsoft.com/ws/2008/06/identity/claims/role",

                                  groupName.Split('\\')[1],

                                  Microsoft.IdentityModel.Claims.ClaimValueTypes.String,

                           SPOriginalIssuers.Format(

                                  SPOriginalIssuerType.TrustedProvider,

                                  SPTrustedIdentityTokenIssuerName));

                               //get the encoded value of what the new role claim will be

                               value = migratedGroupClaim.ToEncodedString();

                           }

                        }

                     }

                     break;

                 case SPWebApplication.AuthenticationMethod.Forms:

                     //code for converting from Forms would be here

                     Debug.WriteLine(oldUser);

                     break;

              }

          }

          catch (Exception ex)

          {

              Debug.WriteLine(ex.Message);

          }

          return value;

       }

   }

}

 

我首先检查传入的 SPWebApplication.AuthenticationMethod 参数的值。 因为我只对转换声明用户感兴趣(从 Windows 到 SAML),我的代码仅在此情形下执行。 如果当前用户是声明用户,我先获得对本地 SPClaimProviderManager 的引用,以便获得用户的声明表示形式。 这样做能让我确定用户是 Windows 声明用户、FBA 声明用户还是 SAML 声明用户。 在我的例子中,我只转换 Windows 声明用户。

 

在我确定存在其中之一后,接下来我要弄明白声明是针对用户还是针对组。 您可能注意到此处颇为怪异。 即使当前用户是 Windows 声明组,传入方法的 isGroup 参数仍返回 false。 这意味着我需要自我检查来搞清楚当前“实体”是用户还是组。 因此我只需看一下声明值 — 采用 domain\user 格式的是用户,采用 SID 格式的是组。

既然我知道实体属于哪个类型,我就可以确定需要哪个声明类型。 对于用户,我需要创建一个标识声明。 有一个要求是我要知道在 Web 应用程序上使用的 SPTrustedIdentityTokenIssuer 的名称。 我可以写出代码来清楚地说明,但这只是个示例,让我偷点懒,嘿嘿,你自己将正确名称传入我的类的构造函数吧。 所以我采用了用户的登录名(域之后的部分),为了我方便一点,他们的电子邮件地址始终将是 loginname@contoso.com。 如果您的组织不采用这种方式,则您需要自己想方法确定正确的电子邮件地址。 我使用它与上面代码来为该用户创建一个标识声明,这就是我返回的值 — 在本例中为 vbtoys\darrins 帐户转换后的形式。 (可能我用词不准确,就别为此难为我啦)

对于组,我采用被分配的 SID 并使用 NTAccount 类来获得该组的友好名称。 我使用它来创建一个新角色声明,然后从其中拉出编码值作为组应该迁移到的值。 (这次用词准确了吧,哈哈!)

另一件值得注意的事情是,无论是对于用户还是组,我都不会自动尝试和迁移一切。 您可能不想迁移某些项,例如服务帐户、内置帐户等,具体取决于您的需求。 不过,此迁移方法相当简洁,您可以根据需要执行任意次数。 您可以只迁移用户的子集,可以分批迁移,可以随着时间的推移逐渐迁移,或以任何方式迁移! 例如,您可能计划迁移一个包含所有用户的数据库。 您可以查询该数据库来获得列表,然后当在您的迁移代码中调用每个用户时,检查它是否在您从数据库获得的用户列表中。 此处仅提供一个示例。

好了,我可不想像 Bryan 和 Raju 那样编写 SDK 文档,我只是想让您至少现在知道如何调用您的类,而不是在那里发愁。 我只不过是写了一个 winforms 应用程序,并对我上述的自定义程序集添加了一个项目引用。 这样非常便于同时生成和调试。 我用来调用我的类和执行迁移的代码大体如下:

 

//get a reference to my web application

SPWebApplication wa = SPWebApplication.Lookup(new Uri("https://foo"));

 

//this is the name of my trusted identity token issuer

string SPTrustedIdentityTokenIssuerName = "ADFSProvider";

//create an instance of the custom migrate user callback class

MigrateUserSample.MigrateTest mt =

new MigrateUserSample.MigrateTest(SPTrustedIdentityTokenIssuerName);

//create an interface reference to it

IMigrateUserCallback muc = mt as IMigrateUserCallback;

//migrate the users with it

wa.MigrateUsers(muc);

 

就这么简单。 针对各种方案需要许多其他类似的解决办法,但这是一个相当好的开端,Bryan 和 Raju 将对此类工作做出更大贡献。

这是一篇本地化的博客文章。请访问 Migrating User Accounts from Windows Claims to SAML Claims 以查看原文