【发布时间】: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 登录时遇到了问题。这就是发生的事情:
- 用户在登录页面上选择 IWA 并点击提交按钮。这通过 ajax 请求发送到常规登录操作。
- 站点收到请求,看到“使用 IWA”选项并重定向到相关操作。已发送 302 响应。
- 浏览器自动处理302响应并调用重定向目标。
- 过滤器发现请求指向 IWA 登录操作,并且 User.Identity.IsAuthenticated == false。已发送 401 响应。
- 浏览器自动处理 401 响应。如果用户尚未在浏览器中使用 IWA 进行身份验证,他们会收到一个弹出窗口来执行此操作(默认浏览器行为)。收到凭据后,浏览器会使用用户凭据执行相同的请求。
- 站点接收经过身份验证的请求并模拟用户对 Active Directory 执行检查。如果用户通过身份验证,我们使用上面的代码完成登录。
- 用户被转发到网站的主页。
- 站点收到加载主页的请求。 这就是事情有时出错的地方。
此时的User.Identity是WindowsIdentity类型,AuthenticationType设置为Negotiate,并且 NOT 如我所料,ClaimsIdentity在上面的SignIn方法中创建。
该站点通过在视图中调用@AntiForgery.GetHtml()为用户准备主页。这样做是为了使用登录用户的详细信息创建一个新的 AntiForgery 令牌。令牌是使用WindowsIdentity 创建的
- 在加载主页时,向服务器发出的 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