我们对 SharePoint 应用程序进行编码,使用证书将其扩展为提供程序托管,并将我们的 MVC 项目锚定到它在展开 SharePoint 的同一 IIS 上展开所有这些内容。
任务 #1:用户登录 Sharepoint,启动我们的应用程序;应用程序在没有任何授权请求的情况下启动,并从它登录的 SharePoint 获取用户。
任务 #2:如果需要 SharePoint 服务请求,我们的应用程序将使用用户在 SharePoint 中登录的相同用户名登录 Sharepoint。
我们尝试了:
1( 构建提供程序托管的应用程序,在其中编写我们的 MVC,创建自唱证书,调整 SharePoint 网站和我们的 MVC 之间的高信任。
我们得到了:如果我们的 MVC 使用 Windows 身份验证,那么在传输到我们的应用程序时,会再次请求用户名和密码;输入它们时,我们可能会使用 GetS2SClientContextWithWindowsIdentity
方法通过TokenHelper
获得ClientContext
。
如果禁用了 Windows 身份验证,则用户不会登录到请求中,并且此方法会响应用户未登录的异常。
2(我们安装并调整了 ADFS,配置了 SharePoint 以使用 ADFS,在中继方信任中写入了 SharePoint 地址和我们的应用程序(在 WS-Federtation 的被动端点中Identifiers and
(
我们得到了:用户登录 Sharepoint,当传输到我们的应用程序时,后者获取用户数据(声明(
至此,第一项任务已经完成。之后,出现了在授权用户下访问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
在 SharePoint STS 中请求授权。然后,我们的应用程序获得了 Cookie Sharepoint,它被锚定到 Sharepoint 服务的查询(CookieContainer FedAuth
中添加(。激活ExecutingWebRequest += ClientContext_ExecutingWebRequest
时,会发生上述情况。
但为此,应该再次使用username
和password
。
如果我们不提交username
和password
,则 ADFS 会以用户数据SecurityToken
响应,以启动应用程序池的名称进行响应。我们需要SecurityToken
登录 SharePoint 的用户。我们还试图发出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 授权所需的响应不同。
在端点中的 ADFS 中,我们调整 URL;SharePoint 授权所需的SecurityToken (wresult)
通过 POST 查询发送给它。问题是我们无法在应用程序中接收此查询,因为它以状态 302 广播并通过 GET 方法重定向到我们的应用程序,而无需与我们的 Cookie SecurityToken
。
问题是:我们如何获取登录 SharePoint 的用户SecurityToken
?
血来潮,但听起来你需要创建一个声明提供程序,这是一个从 SPClaimProvider 继承的类。
SharePoint 中的人员选取器(用于选择人员和组的控件(从声明提供程序获取其所有人员和组。
现成的声明提供程序存在以下声明提供程序,
AllUsersClaimProviderFormsClaimProviderActiveDirectoryClaimsProvider
如果您需要额外的索赔解决方案,则必须编写一个,这还不错。
http://msdn.microsoft.com/en-us/library/office/ee537299(v=office.15(.aspx
基本上,FillClaimsForEntity 是可以向标识主体颁发新声明的地方。
在两个"解决重载"中,可以查看输入是否存在声明匹配项。
例如,假设您在人员选择器中输入"Bob"。 然后,人员选取器使用 SPClaimsOperationManager*(忘记确切的拼写(对服务器场中注册的每个声明提供程序调用 Resolve(字符串输入...(。
因此,假设我们正在谈论表单声明提供程序。 它将检查是否有用户名或电子邮件地址与 Bob 匹配的用户。 对于映射 bob 的每个用户,它将创建一个选取器实体,并将其声明类型设置为 FormsLogonUser,值为 bob 等。 它将每个添加到结果中。
因此,完成后,您将在人员选择器中看到 Bob 已被选中,或者您将看到带有红色下划线的 Bob,表示他们是多个匹配项,您需要选择。
在我看来,您需要构建一个以使人员选择器能够处理您的新声明。
创建并注册声明提供程序后,可以使用人员选取器授予对使用声明的网站的访问权限。
对于一个示例方案,我曾经创建一个声明提供程序,该提供程序根据用户购买的产品发布声明。
所以我做了一个声明类型"http://blah.com/schema/claims/product"(这可以是你想要的任何唯一字符串(,值是"pd.1234.0"之类的东西。现在在FillClaimsForEntities中,我在数据库中查找了实体参数的用户,以获取其所有产品。 然后,我为每个产品创建了一个产品声明,并将其添加到声明列表中。
然后在 Resolve 中,我会查看是否存在具有 ID 或显示名称的产品,以计算解析输入,如果存在,则创建一个选取器实体。
在搜索中也是如此。
然后,我进入了我们的产品页面,并使用人员选择器向拥有该产品声明的任何人授予对该页面的访问权限。"工作方式类似于在表单身份验证中挑选角色"之类的。
还可以对声明层次结构进行编码支持,并且可以在人员选取器中有多个树,每个声明类型对应 1 个树。
例如,说"鲍勃"带回一个人、一本书和一个经理。 你可以有 3 个继承人,1 个用于人员,1 个用于书籍,1 个用于经理等。 并使该类型的声明显示在该树中。
我更进一步,制作了一个拒绝访问处理程序,这样,如果用户因不在声明的产品中而被拒绝访问,它会将他们重定向到该产品购买页面。