且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

登录后刷新AntiForgeryToken

更新时间:2023-12-03 15:52:10

该问题发生,因为防伪标记包含当前已验证用户的用户名。

The issue is occurring because the AntiForgery token contains the username of the currently authenticated user.

因此​​,这里发生了什么:

So here's what happens:


  1. 匿名用户浏览到您的网页

  2. 是评论表单生成一个防伪标记,但此令牌包含一个空的用户名(因为在那一刻的用户是匿名的)

  3. 您正在使用AJAX调用登录

  4. 用户提交评论表单到服务器,因为在初次令牌空的用户名是比当前认证的用户名不同的令牌验证失败。

所以,你有几个选项来解决这个问题:

So you have a couple of options to fix this issue:


  1. 在第3步中不使用AJAX调用。使用标准表单提交登录用户,并重定向他回到最初请求的页面。当然,评论表单将重新加载,并为其生成正确​​的防伪标记。

  2. 刷新防伪标记测井在
  3. 之后

解决方案1.显而易见并没有使它成为一个很好的候选用于覆盖在我的答案。让我们来看看第二个解决方案如何实施。

The obviousness of solution 1. doesn't make it a good candidate for covering it in my answer. Let's see how the second solution could be implemented.

但是,首先让我们来重现问题用一个例子:

But first let's reproduce the problem with an example:

控制器:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        return View();
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Login()
    {
        FormsAuthentication.SetAuthCookie("john", false);
        return Json(new { success = true });
    }

    [HttpPost]
    [ValidateAntiForgeryToken()]
    public ActionResult Comment()
    {
        return Content("Thanks for commenting");
    }
}

〜/查看/主页/ Index.cshtml

<div>
    @{ Html.RenderPartial("_Login"); }
</div>

<div id="comment">
    @{ Html.RenderPartial("_Comment"); }
</div>

<script type="text/javascript">
    $('#loginForm').submit(function () {
        $.ajax({
            url: this.action,
            type: this.method,
            data: $(this).serialize(),
            success: function (result) {
                alert('You are now successfully logged in');
            }
        });
        return false;
    });
</script>

〜/查看/主页/ _Login.cshtml

@using (Html.BeginForm("Login", null, FormMethod.Post, new { id = "loginForm" }))
{
    @Html.AntiForgeryToken()
    <button type="submit">Login</button>
}

〜/查看/主页/ _Comment.cshtml

@using (Html.BeginForm("Comment", null, FormMethod.Post))
{ 
    @Html.AntiForgeryToken()
    <button type="submit">Comment</button>
}

好吧,现在当您导航到主/指数对应的视图将呈现,如果你的评论按钮preSS没有记录,在首先它会工作。但是,如果您登录,然后评论将失败。

Alright now when you navigate to the Home/Index the corresponding view will be rendered and if you press on the Comment button without logging-in first it will work. But if you login and then Comment it will fail.

因此​​,我们可以添加另一个控制器动作,将在以产生新的令牌返回一个简单的 Html.AntiForgeryToken 调用局部视图:

So we could add another controller action that will return a partial view with a simple Html.AntiForgeryToken call in order to generate a fresh token:

public ActionResult RefreshToken()
{
    return PartialView("_AntiForgeryToken");
}

和相应的部分(〜/查看/主页/ _AntiForgeryToken.cshtml

@Html.AntiForgeryToken()

和最后一步是通过更新我们的AJAX调用刷新令牌:

And the final step is to refresh the token by updating our AJAX call:

<script type="text/javascript">
    $('#loginForm').submit(function () {
        $.ajax({
            url: this.action,
            type: this.method,
            data: $(this).serialize(),
            success: function (result) {
                $.get('@Url.Action("RefreshToken")', function (html) {
                    var tokenValue = $('<div />').html(html).find('input[type="hidden"]').val();
                    $('#comment input[type="hidden"]').val(tokenValue);
                    alert('You are now successfully logged in and can comment');
                });
            }
        });
        return false;
    });
</script>