共享点身份验证。如何从 ADFS 获取 SharePoint cookie
Posted
技术标签:
【中文标题】共享点身份验证。如何从 ADFS 获取 SharePoint cookie【英文标题】:Sharepoint authentication. How to get SharePoint cookie from ADFS 【发布时间】:2014-06-21 07:10:38 【问题描述】:我们编写一个 Sharepoint 应用程序,使用证书将其扩展为 Provider-hosted,并将我们的 MVC 项目锚定到它 在扩展 Sharepoint 的同一 IIS 上展开所有这些。
任务 #1: 用户登录 Sharepoint,启动我们的应用程序;应用程序在没有任何授权请求的情况下启动,并从它登录的 Sharepoint 获取用户。
任务 #2:如果需要 Sharepoint 服务请求,我们的应用程序会使用与登录 Sharepoint 的用户相同的用户名登录 Sharepoint。
我们试过了:
1) 构建 Provider-hosted App,在其中编写我们的 MVC,创建自唱证书,在 Sharepoint 站点和我们的 MVC 之间调整 High-trusted。
我们得到了:
如果我们的 MVC 使用 Windows 身份验证,那么在传输到我们的应用程序时,会再次请求用户名和密码;输入时,我们可能会使用GetS2SClientContextWithWindowsIdentity
方法通过TokenHelper
得到ClientContext
。
如果禁用Windows身份验证,则用户未登录Request,此方法响应用户未登录的Exception。
2)
我们安装并调整了 ADFS,配置了 Sharepoint 以与 ADFS 一起工作,在 Relaying Party Trusts 中写入了 Sharepoint 和我们的应用程序的地址(在 Identifiers and
WS-Federtation` Passive Endpoints 中)
我们得到了: 一个用户登录 Sharepoint,当传输到我们的应用程序时,后者获取用户数据(Claims)
至此,第一个任务已经完成。 之后就出现了在授权用户下获取Sharepoint服务的问题
我们试图通过收到的声明为 Sharepoint 获取 AccessToken
我们尝试转移以下声明:
nii":"trusted:adfs
nii":"urn:office:idp:forms:adfs201 //adfs201 - name of our ADFS service
upn:UserLogin
emailaddress:UserEmail@domain.kz
之后,我们根据输入的声明调用了响应 AccessToken
的方法
string issuer = string.IsNullOrEmpty(sourceRealm) ? issuerApplication : string.Format("0@1", issuerApplication, sourceRealm);
string nameid = string.IsNullOrEmpty(sourceRealm) ? sourceApplication : string.Format("0@1", sourceApplication, sourceRealm);
string audience = string.Format("0/1@2", targetApplication, targetApplicationHostName, targetRealm);
List<JsonWebTokenClaim> actorClaims = new List<JsonWebTokenClaim>();
actorClaims.Add(new JsonWebTokenClaim(JsonWebTokenConstants.ReservedClaims.NameIdentifier, nameid));
if (trustedForDelegation && !appOnly)
actorClaims.Add(new JsonWebTokenClaim(TokenHelper.TrustedForImpersonationClaimType, "true"));
if (addSamlClaim)
actorClaims.Add(new JsonWebTokenClaim(samlClaimType, samlClaimValue));
// Create token
JsonWebSecurityToken actorToken = new JsonWebSecurityToken(
issuer: issuer,
audience: audience,
validFrom: DateTime.UtcNow,
validTo: DateTime.UtcNow.AddMinutes(TokenLifetimeMinutes),
signingCredentials: SigningCredentials,
claims: actorClaims);
string actorTokenString = new JsonWebSecurityTokenHandler().WriteTokenAsString(actorToken);
if (appOnly)
// App-only token is the same as actor token for delegated case
return actorTokenString;
List<JsonWebTokenClaim> outerClaims = null == claims ? new List<JsonWebTokenClaim>() : new List<JsonWebTokenClaim>(claims);
outerClaims.Add(new JsonWebTokenClaim(ActorTokenClaimType, actorTokenString));
//****************************************************************************
//SPSAML
if (addSamlClaim)
outerClaims.Add(new JsonWebTokenClaim(samlClaimType, samlClaimValue));
//****************************************************************************
JsonWebSecurityToken jsonToken = new JsonWebSecurityToken(
nameid, // outer token issuer should match actor token nameid
audience,
DateTime.UtcNow,
DateTime.UtcNow.AddMinutes(10),
outerClaims);
string accessToken = new JsonWebSecurityTokenHandler().WriteTokenAsString(jsonToken);
然后,我们尝试获取ClientContext
,使用方法:
GetClientContextWithAccessToken(targetApplicationUri.ToString(), accessToken);
但是我们得到了一个错误报告:
401 Unauthorized
ClientID
和 IssureID
写对了,小写
之后,我们决定在username
和password
的帮助下向ADFS 请求SecurityToken
。收到后,我们使用 SecurityToken
在 SharepointSTS 中请求授权。然后,我们的应用程序获得了 Cookie Sharepoint,它被锚定到查询(在 CookieContainer FedAuth
中添加)到 Sharepoint 服务。激活ExecutingWebRequest += ClientContext_ExecutingWebRequest
时,会出现上述情况。
但是为此,应该使用username
和password
再次请求。
如果我们没有提交username
和password
,则ADFS 会以SecurityToken
响应用户的数据,应用程序池以该用户的名称启动。我们需要 登录 SharePoint 的用户的SecurityToken
。
我们还尝试发出SecurityToken
var session = System.IdentityModel.Services.FederatedAuthentication.SessionAuthenticationModule.CreateSessionSecurityToken(ClientPrincipals, "context", DateTime.UtcNow, System.DateTime.UtcNow.AddHours(1), true);
System.IdentityModel.Services.FederatedAuthentication.SessionAuthenticationModule.AuthenticateSessionSecurityToken(session, true);
但响应与 SharePoint 授权所需的不同。
在 Endpoints 的 ADFS 中,我们调整 URL;我们需要 SharePoint 授权的 SecurityToken (wresult)
通过 POST 查询发送给它。问题是我们无法在应用程序中接收此查询,因为它在状态 302 中广播并通过 GET 方法重定向到我们的应用程序,而没有带有我们的 Cookie 的SecurityToken
。
问题是:我们怎样才能得到登录SharePoint用户的SecurityToken
?
【问题讨论】:
【参考方案1】:在这里一时兴起,但听起来您需要创建一个 Claims Provider,这是一个继承自 SPClaimProvider 的类。
sharepoint 中的人员选择器,这是您用来选择人员和组的控件,它从声明提供者那里获取所有人员和组。
开箱即用,存在以下声明提供程序,
AllUsersClaimProvider FormsClaimsProvider ActiveDirectoryClaimsProvider
如果您需要额外的索赔解决方案,您必须编写一个,这还不错。
http://msdn.microsoft.com/en-us/library/office/ee537299(v=office.15).aspx
基本上,FillClaimsForEntity 是您可以向身份主体发出新声明的地方。
两个 Resolve Overloads 用于查看输入是否存在 Claim 匹配项。
例如假设您在人员选择器中输入“Bob”。然后,人员选取器使用 SPClaimsOperationManager*(忘记确切的拼写),在场中注册的每个索赔提供者上调用 Resolve(string input...)。
假设我们正在谈论表单声明提供程序。它将检查是否存在用户名或电子邮件地址与 Bob 匹配的用户。对于每个映射 bob 的用户,它将创建一个选择器实体并将其声明类型设置为 FormsLogonUser,值为 bob 等。并将每个用户添加到结果中。
因此,完成后,您将在人物选择器中看到 Bob 已被选中,或者您会看到 Bob 带有红色下划线,表示他们有多个匹配项,您需要选择。
在我看来,您需要构建一个以使人员选择器能够处理您的新声明。
一旦您创建并注册了声明提供者,您就可以使用人员选择器通过您的声明授予对网站的访问权限。
举个例子,我曾经做过一个索赔提供商,它根据用户购买的产品发出索赔。
所以我提出了“http://blah.com/schema/claims/product”的声明类型(这可以是您想要的任何唯一字符串)并且值类似于“pd.1234.0”。现在在 FillClaimsForEntities 中,我在数据库中查找用户的实体参数以获取他们的所有产品。然后我为每个产品创建了一个产品声明并将它们添加到声明列表中。
然后在 Resolve 中,我会查看是否存在具有 ID 或显示名称的产品,以计算解析输入,如果存在,则创建一个选择器实体。
搜索中的相同内容。
然后,我进入我们的产品页面,并使用人员选择器向拥有该产品声明的任何人授予访问该页面的权限。 “就像在表单身份验证中选择角色一样”。
您还可以对声明层次结构的支持进行编码,并且您可以在人员选择器中拥有多棵树,您正在使用的每种声明类型对应一棵树。
例如说“鲍勃”带回了一个人、一本书和一个经理。您可以有 3 个层次结构,1 个用于人员,1 个用于书籍,1 个用于管理人员等。然后让该类型的声明显示在该树中。
我更进一步,制作了一个拒绝访问处理程序,这样如果用户因为不在该产品声明中而被拒绝访问,它会将他们重定向到该产品购买页面。
【讨论】:
以上是关于共享点身份验证。如何从 ADFS 获取 SharePoint cookie的主要内容,如果未能解决你的问题,请参考以下文章
使用 ADFS 对 ASP.NET Web Api 进行身份验证
在 javascript 中使用身份 ADFS URL 进行 HTML 页面身份验证
未从 ADFS 3.0 获取用于 OAuth2 访问令牌的 JWT 中的用户身份