【问题标题】:User.Identity fluctuates between ClaimsIdentity and WindowsIdentityUser.Identity 在 ClaimsIdentity 和 WindowsIdentity 之间波动
【发布时间】:2018-05-08 15:16:46
【问题描述】:

我有一个 MVC 站点,它允许使用表单登录和 Windows 身份验证登录。我使用自定义 MembershipProvider 对 Active Directory 的用户进行身份验证、用于 CSRF 保护的 System.Web.Helpers AntiForgery 类和 Owin cookie 身份验证中间件。

在登录期间,一旦用户通过 Active Directory 的身份验证,我会执行以下操作:

IAuthenticationManager authenticationManager = HttpContext.Current.GetOwinContext().Authentication;
authenticationManager.SignOut(StringConstants.ApplicationCookie);
var identity = new ClaimsIdentity(StringConstants.ApplicationCookie,
    ClaimsIdentity.DefaultNameClaimType,
    ClaimsIdentity.DefaultRoleClaimType);
if(HttpContext.Current.User.Identity is WindowsIdentity)
{
    identity.AddClaims(((WindowsIdentity)HttpContext.Current.User.Identity).Claims);
}
else
{
    identity.AddClaim(new Claim(ClaimTypes.Name, userData.Name));
}
identity.AddClaim(new Claim("http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider", "Active Directory"));
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, userData.userGuid));
authenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = false }, identity);

我的 SignOut 函数如下所示:

IAuthenticationManager authenticationManager = HttpContext.Current.GetOwinContext().Authentication;
authenticationManager.SignOut(StringConstants.ApplicationCookie);

登录是通过 jQuery.ajax 请求执行的。成功后,Window.location 会更新到网站的主页。

使用 Forms 和 IntegratedWindowsAuthentication (IWA) 登录都可以,但是我在使用 IWA 登录时遇到了问题。这就是发生的事情:

  1. 用户在登录页面上选择 IWA 并点击提交按钮。这通过 ajax 请求发送到常规登录操作。
  2. 站点收到请求,看到“使用 IWA”选项并重定向到相关操作。已发送 302 响应。
  3. 浏览器自动处理302响应并调用重定向目标。
  4. 过滤器发现请求指向 IWA 登录操作,并且 User.Identity.IsAuthenticated == false。已发送 401 响应。
  5. 浏览器自动处理 401 响应。如果用户尚未在浏览器中使用 IWA 进行身份验证,他们会收到一个弹出窗口来执行此操作(默认浏览器行为)。收到凭据后,浏览器会使用用户凭据执行相同的请求。
  6. 站点接收经过身份验证的请求并模拟用户对 Active Directory 执行检查。如果用户通过身份验证,我们使用上面的代码完成登录。
  7. 用户被转发到网站的主页。
  8. 站点收到加载主页的请求。 这就是事情有时出错的地方
    此时的User.IdentityWindowsIdentity 类型,AuthenticationType 设置为Negotiate,并且 NOT 如我所料,ClaimsIdentity 在上面的SignIn 方法中创建。
    该站点通过在视图中调用@AntiForgery.GetHtml() 为用户准备主页。这样做是为了使用登录用户的详细信息创建一个新的 AntiForgery 令牌。令牌是使用WindowsIdentity
  9. 创建的
  10. 在加载主页时,向服务器发出的 ajax 请求以ClaimsIdentity 到达!因此,第一个到达的POST 请求不可避免地会导致AntiForgeryException,其中它发送的防伪令牌是“为不同的用户”。

刷新页面会导致主页加载 ClaimsIdentity 并允许 POST 请求运行。

第二个相关问题:在刷新后的任何时候,一旦事情正常运行,POST 请求可能会以WindowsIdentity 而不是ClaimsIdentity 到达,再次抛出AntiForgeryException

  • 不是任何特定的发布请求,
  • 不是在任何特定时间后(可能是第一个/第二个请求,可能是百分之一),
  • 不一定在该会话期间第一次调用特定的发布请求。

我觉得我要么遗漏了有关 User.Identity 的内容,要么在登录过程中做错了什么......有什么想法吗?

注意:设置AntiForgeryConfig.SuppressIdentityHeuristicChecks = true; 允许AntiForgery.Validate 操作成功,无论收到WindowsIdentity 还是ClaimsIdentity,但正如MSDN 上所述:

设置此值时要小心。使用不当会打开 应用程序中的安全漏洞。

没有更多的解释,我不知道这里实际打开了哪些安全漏洞,因此不愿意使用它作为解决方案。

【问题讨论】:

  • 你为什么还要费心去投。你可以在没有演员表的情况下做HttpContext.Current.User.Identity.Claims
  • @ScottChamberlain IIdentity 没有 Claims 属性
  • 你使用的是什么版本的 ASP.net?
  • @ScottChamberlain 4.5.2

标签: c# asp.net-mvc owin claims-based-identity windows-identity


【解决方案1】:

原来问题在于 ClaimsPrincipal 支持多个身份。如果您处于具有多个身份的情况,它会自行选择一个。我不知道是什么决定了 IEnumerable 中身份的顺序,但无论它是什么,它显然都必然导致用户会话的生命周期中的顺序保持不变。

如 asp.net/Security git 的问题部分所述,NTLM and cookie authentication #1467:

Identities 包含 windows 标识和 cookie 标识。

您可以使用ClaimsPrincipals 设置一个名为PrimaryIdentitySelectorstatic Func<IEnumerable<ClaimsIdentity>, ClaimsIdentity>,您可以使用它来选择要使用的主要身份。

为此,创建一个带有签名的静态方法:

static ClaimsIdentity MyPrimaryIdentitySelectorFunc(IEnumerable<ClaimsIdentity> identities)

此方法将用于遍历ClaimsIdentitys 列表并选择您喜欢的一个。
然后,在您的 Global.asax.cs 中将此方法设置为 PrimaryIdentitySelector,如下所示:

System.Security.Claims.ClaimsPrincipal.PrimaryIdentitySelector = MyPrimaryIdentitySelectorFunc;

我的 PrimaryIdentitySelector 方法最终看起来像这样:

public static ClaimsIdentity PrimaryIdentitySelector(IEnumerable<ClaimsIdentity> identities)
{
    //check for null (the default PIS also does this)
    if (identities == null) throw new ArgumentNullException(nameof(identities));

    //if there is only one, there is no need to check further
    if (identities.Count() == 1) return identities.First();

    //Prefer my cookie identity. I can recognize it by the IdentityProvider
    //claim. This doesn't need to be a unique value, simply one that I know
    //belongs to the cookie identity I created. AntiForgery will use this
    //identity in the anti-CSRF check.
    var primaryIdentity = identities.FirstOrDefault(identity => {
        return identity.Claims.FirstOrDefault(c => {
            return c.Type.Equals(StringConstants.ClaimTypes_IdentityProvider, StringComparison.Ordinal) &&
                   c.Value == StringConstants.Claim_IdentityProvider;
        }) != null;
    });

    //if none found, default to the first identity
    if (primaryIdentity == null) return identities.First();

    return primaryIdentity;
}

[编辑]
现在,这还不够,因为当Identities 列表中只有一个Identity 时,PrimaryIdentitySelector 似乎没有运行。这会导致登录页面出现问题,有时浏览器会在加载页面时传递 WindowsIdentity,但不会在登录请求中传递它{exasperated sigh}。为了解决这个,我最终为登录页面创建了 ClaimsIdentity,然后手动覆盖线程的 Principal,如 this SO question 中所述。

这会导致 Windows 身份验证出现问题,因为OnAuthenticate 不会发送 401 来请求 Windows 身份。要解决这个,您必须退出登录身份。如果登录失败,请确保重新创建登录用户。 (您可能还需要重新创建 CSRF 令牌)

【讨论】:

    【解决方案2】:

    我不确定这是否会有所帮助,但这就是我为我解决此问题的方法。

    当我添加 Windows 身份验证时,它在 Windows 和声明身份之间波动。我注意到GET 请求得到ClaimsIdentityPOST 请求得到WindowsIdentity。这非常令人沮丧,我决定调试并在DefaultHttpContext.set_User 设置断点。 IISMiddleware 设置了User 属性,然后我注意到它有一个AutomaticAuthentication,默认为true,它设置了User 属性。我把它改成了 false,所以HttpContext.User 一直都变成了ClaimsPrincipal,万岁。

    现在我的问题变成了如何使用 Windows 身份验证。幸运的是,即使我将AutomaticAuthentication 设置为falseIISMiddleware 也会将HttpContext.Features 更新为WindowsPrincipal,所以var windowsUser = HttpContext.Features.Get&lt;WindowsPrincipal&gt;(); 在我的SSO 页面上返回Windows 用户。

    一切正常,没有任何障碍,没有波动,什么都没有。表单库和 Windows 身份验证一起工作。

    services.Configure<IISOptions>(opts =>
    {
        opts.AutomaticAuthentication = false;
    });
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-06-29
      • 2015-07-26
      • 1970-01-01
      • 2013-07-25
      • 2012-07-20
      • 2018-04-25
      • 1970-01-01
      相关资源
      最近更新 更多