如何在 SharePoint 2010 中在声明扩充时获得所有用户声明
原文发布于 2011 年 3 月 30 日(星期三)
在 SharePoint 2010 中执行声明扩充时一个永恒的障碍是,在调用您的自定义声明提供程序执行声明扩充时,尝试找出用户具有哪些声明。 例如,您想要为某个用户扩充的声明可能取决于该用户具有的其他声明的值,即如果用户属于角色“Domain Admins”,则添加角色声明“Uber User”,否则添加声明“Standard User”。 为此困惑了很长一段时间之后,我的好朋友 Israel V. 和 Matt Long 终于想出了解决这个棘手问题的方法(此方法完全可以获得 100% 的肯定,所以谢谢你们!)。
尝试在调用您的声明提供程序进行扩充时在提供的参数之外访问此信息的一个基本问题是,您不具有对 HttpContext 的任何访问权限,因而无法查看声明集。 Israel 和 Matt 弄明白了这个问题并给出了解决方法,即 OperationContext。 那又是什么呢?
别着急,OperationContext 的完整概述如下: https://msdn.microsoft.com/zh-cn/library/system.servicemodel.operationcontext(VS.90).aspx。 简单地说,我们关心的是它是否允许您访问传入的消息标题和属性(对于 SAML 用户)和安全性上下文(对于 Windows 用户)。 它如何帮助我们? 当调用您的自定义声明提供程序进行扩充时,您可以获得 SAML 用户的此传入消息信息,它看起来如下所示:
<s:Envelope xmlns:s="https://www.w3.org/2003/05/soap-envelope" xmlns:a="https://www.w3.org/2005/08/addressing">
<s:Header>
<a:Action s:mustUnderstand="1">https://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue</a:Action>
<a:MessageID>urn:uuid:85a0daaa-2288-4d0a-bda8-5fac05ea61cf</a:MessageID>
<a:ReplyTo>
<a:Address>https://www.w3.org/2005/08/addressing/anonymous</a:Address>
</a:ReplyTo>
<a:To s:mustUnderstand="1">https://localhost:32843/SecurityTokenServiceApplication/securitytoken.svc</a:To>
</s:Header>
<s:Body>
<trust:RequestSecurityToken xmlns:trust="https://docs.oasis-open.org/ws-sx/ws-trust/200512">
<wsp:AppliesTo xmlns:wsp="https://schemas.xmlsoap.org/ws/2004/09/policy">
<a:EndpointReference>
<a:Address>https://fc1/</a:Address>
</a:EndpointReference>
</wsp:AppliesTo>
<trust:KeyType>https://docs.oasis-open.org/ws-sx/ws-trust/200512/Bearer</trust:KeyType>
<trust:OnBehalfOf>
<saml:Assertion MajorVersion="1" MinorVersion="1" AssertionID="_8f1d7b46-2b71-4263-859b-c3e358d7ea84" Issuer="https://myadfsserver/adfs/services/trust" IssueInstant="2011-03-26T18:51:54.671Z" xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion">
<saml:Conditions NotBefore="2011-03-26T18:51:33.198Z" NotOnOrAfter="2011-03-26T19:51:33.198Z">
<saml:AudienceRestrictionCondition>
<saml:Audience>urn:sharepoint:fc1</saml:Audience>
</saml:AudienceRestrictionCondition>
</saml:Conditions>
<saml:AttributeStatement>
<saml:Subject>
<saml:SubjectConfirmation>
<saml:ConfirmationMethod>urn:oasis:names:tc:SAML:1.0:cm:bearer</saml:ConfirmationMethod>
</saml:SubjectConfirmation>
</saml:Subject>
<saml:Attribute AttributeName="emailaddress" AttributeNamespace="https://schemas.xmlsoap.org/ws/2005/05/identity/claims">
<saml:AttributeValue>testuser@vbtoys.com</saml:AttributeValue>
</saml:Attribute>
<saml:Attribute AttributeName="role" AttributeNamespace="https://schemas.microsoft.com/ws/2008/06/identity/claims">
<saml:AttributeValue>pOregon Marketing</saml:AttributeValue>
<saml:AttributeValue>Domain Users</saml:AttributeValue>
<saml:AttributeValue>pSales</saml:AttributeValue>
<saml:AttributeValue>Portal People</saml:AttributeValue>
</saml:Attribute>
<saml:Attribute AttributeName="windowsaccountname" AttributeNamespace="https://schemas.microsoft.com/ws/2008/06/identity/claims">
<saml:AttributeValue>testuser</saml:AttributeValue>
</saml:Attribute>
<saml:Attribute AttributeName="primarysid" AttributeNamespace="https://schemas.microsoft.com/ws/2008/06/identity/claims">
<saml:AttributeValue>testuser@vbtoys.com</saml:AttributeValue>
</saml:Attribute>
</saml:AttributeStatement>
<saml:AuthenticationStatement AuthenticationMethod="urn:federation:authentication:windows" AuthenticationInstant="2011-03-26T18:51:33.069Z">
<saml:Subject>
<saml:SubjectConfirmation>
<saml:ConfirmationMethod>urn:oasis:names:tc:SAML:1.0:cm:bearer</saml:ConfirmationMethod>
</saml:SubjectConfirmation>
</saml:Subject>
</saml:AuthenticationStatement>
<ds:Signature xmlns:ds="https://www.w3.org/2000/09/xmldsig#">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="https://www.w3.org/2001/10/xml-exc-c14n#">
</ds:CanonicalizationMethod>
<ds:SignatureMethod Algorithm="https://www.w3.org/2001/04/xmldsig-more#rsa-sha256">
</ds:SignatureMethod>
<ds:Reference URI="#_8f1d7b46-2b71-4263-859b-c3e358d7ea84">
<ds:Transforms>
<ds:Transform Algorithm="https://www.w3.org/2000/09/xmldsig#enveloped-signature">
</ds:Transform>
<ds:Transform Algorithm="https://www.w3.org/2001/10/xml-exc-c14n#">
</ds:Transform>
</ds:Transforms>
<ds:DigestMethod Algorithm="https://www.w3.org/2001/04/xmlenc#sha256">
</ds:DigestMethod>
<ds:DigestValue>5Qvu+blahblah=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>VUSrynYjN8NOcUexqJOCblahblah</ds:SignatureValue>
<KeyInfo xmlns="https://www.w3.org/2000/09/xmldsig#">
<X509Data>
<X509Certificate>MIIFlzCCBH+gAwIBAgIKHmblahblahblah</X509Certificate>
</X509Data>
</KeyInfo>
</ds:Signature>
</saml:Assertion>
</trust:OnBehalfOf>
<trust:RequestType>https://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue</trust:RequestType>
</trust:RequestSecurityToken>
</s:Body>
</s:Envelope>
<<很抱歉让您阅读如此又长又烦的 Xml,但我认为这是值得的>>
现在,我们已将声明嵌入在 Xml 代码段中,轻松对其解包之后便可在扩充时使用了。 下面我编写了一个小示例演示操作过程:
using System.Xml;
…
private class IncomingClaim
{
public string claimType { get; set; }
public string claimValue { get; set; }
public IncomingClaim(string claimType, string claimValue)
{
this.claimType = claimType;
this.claimValue = claimValue;
}
}
protected override void FillClaimsForEntity(Uri context, SPClaim entity,
List<SPClaim> claims)
{
//get the request envelope with the claims information
string rqst =
System.ServiceModel.OperationContext.Current.RequestContext.RequestMessage.ToString();
//create a list to store the results
List<IncomingClaim> incomingClaims = new List<IncomingClaim>();
//create an Xml document for parsing the results and load the data
XmlDocument xDoc = new XmlDocument();
xDoc.LoadXml(rqst);
//create a new namespace table so we can query
XmlNamespaceManager xNS = new XmlNamespaceManager(xDoc.NameTable);
//add the namespaces we'll be using
xNS.AddNamespace("s", "https://www.w3.org/2003/05/soap-envelope");
xNS.AddNamespace("trust", "https://docs.oasis-open.org/ws-sx/ws-trust/200512");
xNS.AddNamespace("saml", "urn:oasis:names:tc:SAML:1.0:assertion");
//get the list of claim nodes
XmlNodeList xList =
xDoc.SelectNodes("s:Envelope/s:Body/trust:RequestSecurityToken/trust:OnBehalfOf/saml:Assertion/saml:AttributeStatement/saml:Attribute", xNS);
//get the matches
if ((xList != null) && (xList.Count > 0))
{
//enumerate through the matches to get the claim list
foreach (XmlNode xNode in xList)
{
//get the claim type first
string currentClaimType =
xNode.Attributes["AttributeNamespace"].Value +
"/" + xNode.Attributes["AttributeName"].Value;
//each claim type will have one to many child nodes with the values
foreach (XmlNode claimNode in xNode.ChildNodes)
{
incomingClaims.Add(new IncomingClaim(currentClaimType,
claimNode.InnerText));
}
}
}
//now you can do whatever you want with the list of claims and finish
//your augmentation
…
}
就这么多了。 代码非常简单,并详细地进行了备注,我相信如果您仔细看过,并将 Xml 粘贴到正常的 Xml 编辑器(如 Visual Studio .NET 附带的编辑器)中,就应该很清楚它是如何工作的。 到此并不是很深奥,只不过是 Xml 而已。
博客读者 Luis A. 指出基于此方法实施还有一个有趣的副作用。 您在 RequestMessage 中获得的声明集包括来自 IP-STS 的所有内容,即包括不存在映射的对应声明时 SharePoint STS 要弃置的所有声明。 所以,如果您知道 SharePoint 要弃置哪些声明并且您想无论如何也要保留它们,则只需使用自定义的声明提供程序重新对其扩充即可。 只需从 RequestMessage 提取声明的值和类型,然后再重新添加它们即可。
Windows 声明用户不会将此消息与他们的请求关联,但是您可以使用 OperationContext 的 SecurityContext 获取 WindowsIdentity。 然后您便可执行 WindowsIdentity 可以做的任何事情,如获取用户所属组的列表等。 下面是有关该内容的示例:
//do windows
WindowsIdentity wid =
System.ServiceModel.OperationContext.Current.ServiceSecurityContext.WindowsIdentity;
//get a reference to the groups to which the user belongs
IdentityReferenceCollection grps = wid.Groups;
//enumerate each group (which will be represented as a SID)
//NOTE that this includes ALL groups - builtin, local, domain, etc.
foreach (IdentityReference grp in grps)
{
//get the plain name of the group
SecurityIdentifier sid = new SecurityIdentifier(grp.Value);
NTAccount groupAccount = (NTAccount)sid.Translate(typeof(NTAccount));
string groupName = groupAccount.ToString();
//for domain groups remove the domain\ prefix from the group
//name in order to have feature parity with SAML users
if (groupName.Contains("\\"))
groupName = groupName.Substring(groupName.IndexOf("\\") + 1);
//add the claim
incomingClaims.Add(new
IncomingClaim("https://schemas.microsoft.com/ws/2008/06/identity/claims/role",
groupName));
}
您可能想要确保为 System.Security.Principal 添加 using 语句以使 WindowsIdentity、IdentityReference、NTAccount 等正确解析。 否则代码同样应该相当简单。 我只是获取用户的组列表并将其作为标准角色声明放入我的自定义声明集中 。
再次感谢 Israel 和 Matt 共享该超级有价值的信息。
这是一篇本地化的博客文章。请访问 How to Get All User Claims at Claims Augmentation Time in SharePoint 2010 以查看原文