反伪造令牌用于用户“”,但当前用户是“用户名”

我正在构build一个单页面应用程序,并遇到了防伪标记的问题。

我知道为什么这个问题发生,我只是不知道如何解决这个问题。

发生以下情况时出现错误:

  1. 未login的用户加载对话框(使用生成的防伪标记)
  2. 用户closures对话框
  3. 用户login
  4. 用户打开相同的对话框
  5. 用户在对话框中提交表单

反伪造令牌用于用户“”,但当前用户是“用户名”

发生这种情况的原因是因为我的应用程序是100%单页面,当用户通过ajax postlogin到/Account/JsonLogin ,我简单地使用从服务器返回的“已validation视图”切换当前视图,但不要重新加载页面。

我知道这是原因,因为如果我简单地重新加载步骤3和4之间的页面,没有错误。

因此,加载的表单中的@Html.AntiForgeryToken()似乎仍然会为旧用户返回一个令牌,直到页面重新加载。

我怎样才能改变@Html.AntiForgeryToken()为新的authentication用户返回一个令牌?

我在每个Application_AuthenticateRequest上注入了一个新的GenericalPrincipal ,所以在@Html.AntiForgeryToken()被调用的时候, HttpContext.Current.User.Identity实际上是我的自定义标识, IsAuthenticated属性被设置为true,但是@Html.AntiForgeryToken似乎仍然为旧用户呈现一个令牌,除非我做一个页面重新加载。

发生这种情况的原因是防伪令牌embedded用户的用户名作为encryption令牌的一部分,以便进行更好的validation。 当你第一次调用@Html.AntiForgeryToken() ,用户没有login,因此令牌会有一个空的用户名string,在用户login后,如果你不replace防伪令牌,它将不会通过validation因为最初的令牌是匿名用户,现在我们有一个已知用户名的已authentication用户。

你有几个select来解决这个问题:

  1. 就在这时候,让你的SPA做一个完整的POST,当页面重新加载时,它将有一个embedded了更新后的用户名的反伪造令牌。

  2. 使用@Html.AntiForgeryToken()进行部分查看,并在login之后,再次执行AJAX请求,并用请求的响应replace现有的防伪标记。

  3. 只需禁用身份检查防伪validation执行。 将以下内容添加到您的Application_Start方法: AntiForgeryConfig.SuppressIdentityHeuristicChecks = true

要修复错误,您需要将OutputCache Data Annotation放置在Login页面的Get ActionResult ,如下所示:

 [OutputCache(NoStore=true, Duration = 0, VaryByParam= "None")] public ActionResult Login(string returnUrl) 

我有同样的问题,这个肮脏的黑客得到修复,至less直到我可以用更干净的方式修复它。

  public ActionResult Login(string returnUrl) { if (AuthenticationManager.User.Identity.IsAuthenticated) { AuthenticationManager.SignOut(); return RedirectToAction("Login"); } 

它发生了很多次,我的应用程序,所以我决定谷歌!

我发现了一个关于这个错误的简单解释! 用户正在双击该button进行login! 您可以在下面的链接中看到另一个用户在讨论这个问题:

MVC 4提供的防伪标记是针对用户“”的,但当前用户是“用户”

我希望它有帮助! =)

在注册过程中,我有一个相当特殊的问题。 一旦用户点击发送给他们的电子邮件链接,他们将login并直接发送到帐户详细信息屏幕,以填写更多信息。 我的代码是:

  Dim result = Await UserManager.ConfirmEmailAsync(userId, code) If result.Succeeded Then Dim appUser = Await UserManager.FindByIdAsync(userId) If appUser IsNot Nothing Then Dim signInStatus = Await SignInManager.PasswordSignInAsync(appUser.Email, password, True, shouldLockout:=False) If signInStatus = SignInStatus.Success Then Dim identity = Await UserManager.CreateIdentityAsync(appUser, DefaultAuthenticationTypes.ApplicationCookie) AuthenticationManager.SignIn(New AuthenticationProperties With {.IsPersistent = True}, identity) Return View("AccountDetails") End If End If End If 

我发现返回视图(“AccountDetails”)给我的令牌exception,我猜是因为ConfirmEmail函数用AllowAnonymous装饰,但AccountDetails函数有ValidateAntiForgeryToken。

更改返回到RedirectToAction(“AccountDetails”)解决了我的问题。

我有单页ASP.NET MVC核心应用程序相同的问题。 我通过在所有控制器动作中设置HttpContext.User来解决这个问题,这些动作改变了当前的身份声明(因为MVC只对后续的请求做这个,正如这里所讨论的)。 我使用了一个结果filter而不是中间件来将防伪cookie添加到我的响应中,这确保了它们仅在MVC操作返回后才生成。

控制器(注意:我正在用ASP.NET Core Identitypipe理用户):

 [Authorize] [ValidateAntiForgeryToken] public class AccountController : Controller { private SignInManager<IdentityUser> signInManager; private UserManager<IdentityUser> userManager; private IUserClaimsPrincipalFactory<IdentityUser> userClaimsPrincipalFactory; public AccountController(SignInManager<IdentityUser> signInManager, UserManager<IdentityUser> userManager, IUserClaimsPrincipalFactory<ApplicationUser> userClaimsPrincipalFactory) { this.signInManager = signInManager; this.userManager = userManager; this.userClaimsPrincipalFactory = userClaimsPrincipalFactory; } [HttpPost] [AllowAnonymous] public async Task<IActionResult> Login(string username, string password) { if (username == null || password == null) { return BadRequest(); // Alias of 400 response } var result = await signInManager.PasswordSignInAsync(username, password, false, lockoutOnFailure: false); if (result.Succeeded) { var user = await userManager.FindByNameAsync(username); // Must manually set the HttpContext user claims to those of the logged // in user. Otherwise MVC will still include a XSRF token for the "null" // user and token validation will fail. (MVC appends the correct token for // all subsequent reponses but this isn't good enough for a single page // app.) var principal = await userClaimsPrincipalFactory.CreateAsync(user); HttpContext.User = principal; return Json(new { username = user.UserName }); } else { return Unauthorized(); } } [HttpPost] public async Task<IActionResult> Logout() { await signInManager.SignOutAsync(); // Removing identity claims manually from the HttpContext (same reason // as why we add them manually in the "login" action). HttpContext.User = null; return Json(new { result = "success" }); } } 

结果filter添加防伪cookie:

 public class XSRFCookieFilter : IResultFilter { IAntiforgery antiforgery; public XSRFCookieFilter(IAntiforgery antiforgery) { this.antiforgery = antiforgery; } public void OnResultExecuting(ResultExecutingContext context) { var HttpContext = context.HttpContext; AntiforgeryTokenSet tokenSet = antiforgery.GetAndStoreTokens(context.HttpContext); HttpContext.Response.Cookies.Append( "MyXSRFFieldTokenCookieName", tokenSet.RequestToken, new CookieOptions() { // Cookie needs to be accessible to Javascript so we // can append it to request headers in the browser HttpOnly = false } ); } public void OnResultExecuted(ResultExecutedContext context) { } } 

Startup.cs摘录:

 public partial class Startup { public Startup(IHostingEnvironment env) { //... } public IConfigurationRoot Configuration { get; } public void ConfigureServices(IServiceCollection services) { //... services.AddAntiforgery(options => { options.HeaderName = "MyXSRFFieldTokenHeaderName"; }); services.AddMvc(options => { options.Filters.Add(typeof(XSRFCookieFilter)); }); services.AddScoped<XSRFCookieFilter>(); //... } public void Configure( IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { //... } } 
 [OutputCache(NoStore=true, Duration = 0, VaryByParam= “None”)] public ActionResult Login(string returnUrl) 

您可以通过在“login(Get)”操作的第一行中放置一个断点来testing这一点。 在添加OutputCache指令之前,断点将在第一次加载时被命中,但是在点击浏览器后退button之后它不会。 在添加指令后,您应该每次都要触发断点,所以AntiForgeryToken将成为正确的,而不是空的。

在internet-shop中有一个反伪造令牌validation的问题:用户打开很多标签(有商品),login后尝试login另一个,得到这样的AntiForgeryException。 所以,AntiForgeryConfig.SuppressIdentityHeuristicChecks = true对我没有帮助,所以我使用了这样丑陋的hackfix,也许对某个人有帮助:

  public class ExceptionPublisherExceptionFilter : IExceptionFilter { public void OnException(ExceptionContext exceptionContext) { var exception = exceptionContext.Exception; var request = HttpContext.Current.Request; if (request != null) { if (exception is HttpAntiForgeryException && exception.Message.ToLower().StartsWith("the provided anti-forgery token was meant for user \"\", but the current user is")) { var isAjaxCall = string.Equals("XMLHttpRequest", request.Headers["x-requested-with"], StringComparison.OrdinalIgnoreCase); var returnUrl = !string.IsNullOrWhiteSpace(request["returnUrl"]) ? request["returnUrl"] : "/"; var response = HttpContext.Current.Response; if (isAjaxCall) { response.Clear(); response.StatusCode = 200; response.ContentType = "application/json; charset=utf-8"; response.Write(JsonConvert.SerializeObject(new { success = 1, returnUrl = returnUrl })); response.End(); } else { response.StatusCode = 200; response.Redirect(returnUrl); } } } ExceptionHandler.HandleException(exception); } } public class FilterConfig { public static void RegisterGlobalFilters(GlobalFilterCollection filters) { filters.Add(new ExceptionPublisherExceptionFilter()); filters.Add(new HandleErrorAttribute()); } } 

认为如果可以设置防伪标记生成选项,排除用户名或类似的东西就会很好。

Interesting Posts