出现此问题的原因是 AntiForgery 令牌包含当前经过身份验证的用户的用户名。
所以会发生这样的事情:
- 匿名用户导航到您的页面
- 为评论表单生成防伪令牌,但该令牌包含空用户名(因为此时用户是匿名的)
- 您正在使用 AJAX 调用登录
- 用户向服务器提交评论表单,但令牌验证失败,因为初始令牌中包含的空用户名与当前经过身份验证的用户名不同。
因此,您有几个选项可以解决此问题:
- 在步骤 3. 中,不要使用 AJAX 调用。使用标准表单提交来登录用户并将其重定向回最初请求的页面。当然,评论表单将被重新加载并为其生成正确的防伪令牌。
- 登录后刷新防伪令牌
解决方案 1. 的显而易见性并不使它成为我的答案中涵盖它的良好候选者。让我们看看如何实施第二种解决方案。
但首先让我们用一个例子来重现这个问题:
控制器:
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");
}
}
~/Views/Home/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>
~/Views/Home/_Login.cshtml
:
@using (Html.BeginForm("Login", null, FormMethod.Post, new { id = "loginForm" }))
{
@Html.AntiForgeryToken()
<button type="submit">Login</button>
}
~/Views/Home/_Comment.cshtml
:
@using (Html.BeginForm("Comment", null, FormMethod.Post))
{
@Html.AntiForgeryToken()
<button type="submit">Comment</button>
}
好吧,现在当您导航到主页/索引时,将呈现相应的视图,如果您在不先登录的情况下按“评论”按钮,它将起作用。但如果你登录然后评论就会失败。
因此,我们可以添加另一个控制器操作,该操作将通过简单的操作返回部分视图Html.AntiForgeryToken
调用以生成新令牌:
public ActionResult RefreshToken()
{
return PartialView("_AntiForgeryToken");
}
和相应的部分(~/Views/Home/_AntiForgeryToken.cshtml
):
@Html.AntiForgeryToken()
最后一步是通过更新 AJAX 调用来刷新令牌:
<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>