防伪令牌是为不同的基于声明的用户而设计的

38

我正在为我们使用ASP.NET身份验证登录的应用程序开发登出功能。我可以成功登录,但是当我注销并尝试再次登录时,会收到以下消息:

The provided anti-forgery token was meant for a different claims-based user than the current user.

这是我的登出代码:

 public ActionResult Logout()
        {
            SignInManager.Logout();
            return View("Index"); 
        }

**SignInManager.cs**
 public void Logout()
        {
            AuthenticationManager.SignOut(); 
        }

用户点击注销按钮后,将被带到登录界面。URL仍然显示为“http://localhost:8544/Login/Logout”。由于我们正在登录页面,也许应该只显示“http://localhost:8544/Login”。


1
我认为你可以在注销操作中使用重定向来使其正常工作,而不是直接返回视图。 return RedirectToAction("Index"); 这样做有望设置所有标头和 cookie 信息。 - Silvermind
在我的情况下,这是EDMX文件的一些问题。一旦我更新了所有表格,问题就解决了。 - NoWar
7个回答

22

对我有效的方法是交换中间件使用的顺序。首先添加 app.UseAuthentication() 然后再加上防伪标记。这是我的做法:

app.UseAuthentication();
app.Use(next => ctx =>
        {
            var tokens = antiforgery.GetAndStoreTokens(ctx);

            ctx.Response.Cookies.Append("XSRF-TOKEN", tokens.RequestToken,
                new CookieOptions() { HttpOnly = false });

            return next(ctx);
});

反过来做会创建一个不适用于已认证用户的令牌。


这解决了我在 .net core identity 上的问题。相当令人疯狂。 - Jonathan Galentine
谢谢!这个序列的东西解决了我的Asp.Net Core 2.1问题。 - Mayank Gupta
谢谢,它起作用了。问题解决了,节省了很多时间。 - Sreeraj_ms
是的,它起作用了,但付出了一整天的时间! - user1689571

18

你返回了一个View,而没有调用RedirectToAction()。所以发生的情况是视图在注销请求的上下文中运行,用户仍然登录。只有在请求完成后,他们才会退出登录。

所以,请尝试

public ActionResult Logout()
{
    SignInManager.Logout();
    return RedirectToAction("Index", "Home");
}

12

我发现用户在已经通过身份验证后提交登录页面时会遇到此问题。我通过以下步骤再现了此错误:

  1. 登录并打开两个标签页,
  2. 从其中一个标签页注销,
  3. 重新加载两个标签页,
  4. 在其中一个标签页上登录,
  5. 尝试使用另一个标签页进行登录。该错误出现在 POST: /Account/Login 操作之前。

我的大多数用户都使用移动设备访问 Web 应用程序,因此他们将登录页面添加到书签中,并在已经登录的背景标签页中打开它并进行提交。我还推测有时他们会打开一个包含登录表单的未激活标签页,然后再提交。

我意识到解决此问题的方法有很多种。我通过以下两个更改来解决这个问题:

  1. 我在“GET:/Account/Login”操作中添加了一个检查 User.Identity.IsAuthenticated 的语句:
if (User.Identity.IsAuthenticated)
{
   try
   {
      return RedirectToLocal(returnUrl);
   }
   catch
   {
      return RedirectToAction("index", "Home");
   }
}
  • 在我的控制器中,我创建了一个名为“检查是否已登录”的操作:
  • [AllowAnonymous]
    public JsonResult CheckLogedIn()
    {
        try
        {
            return Json(new { logged_in = User.Identity.IsAuthenticated }, JsonRequestBehavior.AllowGet);
        }
        catch
        {
            return Json(new { logged_in = false }, JsonRequestBehavior.AllowGet);
        }
    }
    

    当我已经登录时,我在视图中重复调用它,以将所有打开的登录表单重定向到其他页面:

    <script type="text/javascript">
        setInterval(function () {
            $.ajax({
                    url: '@Url.Action("CheckLogedIn", "Account")',
                    type: "GET",
                }).done(function (data) {
                    if (data.logged_in) {
                        window.location = '/';
                    }
                });
        }, 5000);  
    </script>
    

    这对我很有效,希望能对你有所帮助。


    1
    谢谢您的解释!我在我们的日志中时不时地看到这个错误,但还没有能够复现它。由于您的回答,我现在能够这样做了 - 而且可能会修复那个错误 :-) - Jan Köhler
    1
    谢谢。我已经使用了几年,但一直无法弄清楚。这个解决了问题。 - Stackedup
    如果我需要修改这个过程,我会尝试使用广播频道API:https://www.smashingmagazine.com/2022/09/javascript-api-guide/#broadcast-channel-api - Justin

    12

    试一试:

    public ActionResult Logout()
    {
        AuthenticationManager.SignOut();
        Session.Abandon();
        return RedirectToAction("Index");
    }
    

    这将重新加载您的登录页面,从而为您提供一个新的 CSRF 令牌。


    7
    ASP.NET身份验证与会话没有任何关联 :) - blowdart
    3
    @blowdart 是的,但根据应用程序的不同,清除会话变量可能是有意义的,特别是在注销用户时与该用户的会话相关联。 - Mark1270287

    10

    我一直在登录时遇到这个错误很长一段时间了,但一直无法找出原因。最终我找到了它,并在这里发布(虽然原因略有不同),以防其他人也遇到同样的问题。

    这是我的代码:

    //
    // GET: /login
    [OutputCache(NoStore = true, Location = System.Web.UI.OutputCacheLocation.None)]
    public ActionResult Login()
    {
        return View();
    }
    
    //
    // POST: /login
    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
    {
        AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
    
        if (!ModelState.IsValid)
        {
            return View(model);
        }
        //etc...
    

    99.99%的登录都正常工作,但每隔一段时间就会出现上述错误,尽管我无法复制它,直到现在。

    只有在快速连续两次点击登录按钮时才会发生此错误。但是,如果我在Login动作中删除AuthenticationManager.SignOut行,则没有问题。我不确定为什么要加入那行代码,但它导致了问题-删除它可以解决问题。


    1

    在我的登录方法中,我没有像Sean提到的那样拥有AuthenticationManager.SignOut命令。我能够通过在下一个视图加载之前多次单击登录按钮来重现该问题。我在第一次单击后禁用了登录按钮以防止错误发生。

    <button type="submit" onclick="this.disabled=true;this.form.submit();"/>
    

    -3

    试试这个:

     public ActionResult Login(string modelState = null)
        {
            if (modelState != null)
                ModelState.AddModelError("", modelState );
    
            return View();
        }
     [ValidateAntiForgeryToken]
        public async Task<ActionResult> Login(LoginViewModel model)
        {
            AuthenticationManager.SignOut();
    
            return RedirectToAction("Login", "Controller", new { modelState = "MSG_USER_NOT_CONFIRMED" });
        }
    

    3
    欢迎来到 Stack Overflow!请不要仅凭源代码进行回答。尽量提供关于您解决方案的好描述。请参阅:如何编写好的答案?。谢谢! - sɐunıɔןɐqɐp

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接