将用户帐户从 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 以查看原文