登录后重新加载AntiForgeryToken

在成功登录同一页面中的另一个视图后,我需要在位于视图中的表单中重新加载AntiForgeryToken。

我可以通过jQuery在结果登录页面输入@ Html.AntiForgeryToken()键的forms更新吗?

如果是,这是推荐和安全吗?

我该怎么做?

编辑:

在布局中我有不同的PartialViews:

部分登录:

 

而在另一个部分,发送评论的能力:

 
@{ Html.RenderAction(MVC.Comment.Create()); }

要发送评论,用户必须登录,因此登录后,评论表格需要更新AntiForgeryToken,否则我会收到validation错误,因为现在登录已经不同了。

谢谢

出现此问题的原因是AntiForgery令牌包含当前经过身份validation的用户的用户名。

所以这是发生的事情:

  1. 匿名用户导航到您的页面
  2. 为评论表单生成防伪令牌,但此令牌包含空用户名(因为此时用户是匿名的)
  3. 您正在使用AJAX调用登录
  4. 用户将注释表单提交给服务器,并且令牌validation失败,因为初始令牌中包含的空用户名与当前经过身份validation的用户名不同。

因此,您有几个选项可以解决此问题:

  1. 在步骤3.不要使用AJAX调用。 使用标准表单提交登录用户并将其重定向回最初请求的页面。 评论表格当然会被重新加载并为其生成正确的防伪标记。
  2. 登录后刷新防伪令牌

解决方案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

 
@{ Html.RenderPartial("_Login"); }
@{ Html.RenderPartial("_Comment"); }

~/Views/Home/_Login.cshtml

 @using (Html.BeginForm("Login", null, FormMethod.Post, new { id = "loginForm" })) { @Html.AntiForgeryToken()  } 

~/Views/Home/_Comment.cshtml

 @using (Html.BeginForm("Comment", null, FormMethod.Post)) { @Html.AntiForgeryToken()  } 

好了,现在当您导航到Home / Index时,将呈现相应的视图,如果您在没有登录的情况下按下Comment按钮,它将起作用。 但如果你登录然后评论它将失败。

所以我们可以添加另一个控制器动作,它将返回一个带有简单Html.AntiForgeryToken调用的局部视图,以生成一个新的令牌:

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

和相应的部分( ~/Views/Home/_AntiForgeryToken.cshtml ):

 @Html.AntiForgeryToken() 

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

  

您可以通过在登录后返回AntiForgeryToken来实现此目的。

无需重复使用相同的令牌2次。

控制器:

 [HttpPost] [ValidateAntiForgeryToken] public ActionResult Login(LoginModel model) { // do something with login // return new token as a partial to parse and get value return this.PartialView("_AntiForgeryPartial"); } 

_AntiForgeryPartial:

 @Html.AntiForgeryToken() 

您可以使用与此类似的JS仅将新的AntiForgeryToken值加载到注释表单中。

视图:

 $("#LoginForm").submit(function (e) { e.preventDefault(); var $this = $(this); $.ajax({ type: $this.attr("method"), url: $this.attr("action"), data: $this.serialize(), success: function (response) { // get the new token from the response html var val = $(response).find('input[type="hidden"]').val(); // set the new token value $('.commentsform input[type="hidden"]').val(val); } }); }); 

当评论表单执行POST时,您应该能够针对新的唯一AntiForgeryToken进行validation。

Steven Sanderson在AntiForgeryToken()上有一篇很棒的文章 ,如果你想了解更多如何使用它以及它的用途。