c# - 共享点身份验证。如何从 ADFS 获取 SharePoint cookie

标签 c# asp.net-mvc sharepoint adfs2.0

我们编写一个 Sharepoint 应用程序,将其扩展为提供商托管,使用证书,并将我们的 MVC 项目锚定到它 在扩展 Sharepoint 的同一个 IIS 上扩展所有这些。

任务#1:用户登录 Sharepoint,启动我们的应用程序;应用程序在没有任何授权请求的情况下启动,并从它登录的 Sharepoint 获取用户。

任务 #2:如果需要 Sharepoint 服务请求,我们的应用程序将使用与登录 Sharepoint 的用户相同的用户名登录 Sharepoint。

我们尝试过:

1) 构建提供商托管的应用程序,在其中编写我们的 MVC,创建自签名证书,在 Sharepoint 站点和我们的 MVC 之间调整高信任度。

我们得到了: 如果我们的 MVC 使用 Windows 身份验证,那么在传输到我们的应用程序时,将重新请求用户名和密码;输入它们时,我们可以使用GetS2SClientContextWithWindowsIdentity方法通过TokenHelper获取ClientContext

如果禁用了 Windows 身份验证,则用户未登录 Request,此方法将响应用户未登录的异常。

2) 我们安装并调整了 ADFS,配置 Sharepoint 以与 ADFS 一起使用,在中继方信任中写入 Sharepoint 和我们的应用程序的地址(在 Identifiers 和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

之后,我们根据输入的Claims调用了一个方法来响应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

ClientIDIssureID 写对了,小写

在那之后,我们决定借助用户名密码从ADFS请求SecurityToken。收到它后,我们使用 SecurityToken 在 SharepointSTS 中请求授权。然后,我们的应用程序获得了 Cookie Sharepoint,它被锚定到对 Sharepoint 服务的查询(添加在 CookieContainer FedAuth 中)。当激活 ExecutingWebRequest += ClientContext_ExecutingWebRequest 时,会发生上述情况。

但是为此,应该使用用户名密码再次请求。

如果我们不提交usernamepassword,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 授权所需的响应不同。

在ADFS in Endpoints中,我们调整URL; SharePoint 授权所需的 SecurityToken (wresult) 通过 POST 查询发送给它。问题是我们无法在应用程序中接收此查询,因为它在状态 302 中广播并通过 GET 方法重定向到我们的应用程序,没有 SecurityToken 和我们的 Cookie。

问题是如何获取登录SharePoint的用户的SecurityToken

最佳答案

在这里一时兴起,但听起来您需要创建一个声明提供程序,这是一个继承自 SPClaimProvider 的类。

SharePoint 中的人物选择器,这是您用来选择人物和群组的控件,所有这些人物和群组都来自 Claim Providers。

开箱即用,存在以下 Claim Provider,

AllUsersClaimProvider FormsClaimsProvider ActiveDirectoryClaimsProvider

如果您需要额外的 claim 解决方案,您必须写一个,这还不错。

http://msdn.microsoft.com/en-us/library/office/ee537299(v=office.15).aspx

基本上,FillClaimsForEntity 是您可以向身份主体发出新声明的地方。

两个 Resolve Overloads 是您查看输入是否存在 Claim 匹配的地方。

例如假设您在人员选择器中输入“Bob”。然后,人员选择器使用 SPClaimsOperationManager*(忘记确切的拼写),对场中注册的每个 claim 提供者调用 Resolve(字符串输入...)。

假设我们正在谈论 Forms Claims Provider。它会检查是否有用户名或电子邮件地址与 Bob 匹配。对于每个用户映射 bob,它将创建一个选择器实体并将其声明类型设置为 FormsLogonUser,值为 bob 等。并将每个添加到结果中。

因此,完成后,您会在人员选择器中看到 Bob 已被选中,或者您会看到 Bob 带有红色下划线,表示他们是多个匹配项,您需要选择。

在我看来,您需要构建一个以使人员选择器能够处理您的新声明。

创建并注册声明提供者后,您可以使用人员选择器使用您的声明授予对网站的访问权限。

举个例子,我曾经制作了一个 claim 提供者,它根据用户购买的产品发出 claim 。

所以我声明类型为“http://blah.com/schema/claims/product”(这可以是您想要的任何唯一字符串)并且值类似于“pd.1234.0”。现在,在 FillClaimsForEntities 中,我在我们的数据库中查找了实体参数上的用户,以获取他们的所有产品。然后我为每个产品创建了一个产品声明并将它们添加到声明列表中。

然后在 Resolve 中,我会查看是否存在具有 ID 或显示名称的产品,该 ID 或显示名称对解析输入进行数学运算,如果存在,则创建一个选择器实体。

在搜索中也是如此。

然后我进入我们的产品页面,并使用人员选择器向任何拥有该产品声明的人授予访问该页面的权限。 “就像在表单例份验证中选择角色一样工作”。

您还可以编写代码支持 Claims Heirarchy,并且您可以在人员选择器中拥有多棵树,您正在使用的每种声明类型对应一棵树。

例如说“Bob”会带回一个人、一本书和一位经理。您可以有 3 个层次结构,1 个用于人员,1 个用于书籍,1 个用于经理,等等。并使该类型的声明显示在该树中。

我更进一步,制作了一个拒绝访问处理程序,这样如果用户因不在声明的产品中而被拒绝访问,它会将他们重定向到该产品购买页面。

关于c# - 共享点身份验证。如何从 ADFS 获取 SharePoint cookie,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23464751/

相关文章:

c# - 启用 bool 值并在 View 中输入文本,然后传回 Controller - MVC

c# - WPF 依赖属性中的每个属性都是?

database - 如何将 MVC4、DropDownList 和 C# 类结合在一起?

c# - 接口(interface)中的类型约束适用于基类

javascript - 带有级联下拉列表的搜索表单在 asp.net mvc 中不起作用

c# - 自定义列表中的 SharePoint 自定义上下文菜单出现在文件夹上,而不仅仅是文件

sharepoint - 创建 Sharepoint/MOSS 站点地图

c# - 如何使用 MS Graph 将 Azure 门户注册应用程序的范围限制为只能访问 sharepoint 中的特定日历

C# 属性和 "tostring"方法

c# - 使用 Linq 选择对象上的所有列