登录后重新加载AntiForgeryToken
在成功登录同一页面中的另一个视图后,我需要在位于视图中的表单中重新加载AntiForgeryToken。
我可以通过jQuery在结果登录页面输入@ Html.AntiForgeryToken()键的forms更新吗?
如果是,这是推荐和安全吗?
我该怎么做?
编辑:
在布局中我有不同的PartialViews:
部分登录:
而在另一个部分,发送评论的能力:
@{ Html.RenderAction(MVC.Comment.Create()); }
要发送评论,用户必须登录,因此登录后,评论表格需要更新AntiForgeryToken,否则我会收到validation错误,因为现在登录已经不同了。
谢谢
出现此问题的原因是AntiForgery令牌包含当前经过身份validation的用户的用户名。
所以这是发生的事情:
- 匿名用户导航到您的页面
- 为评论表单生成防伪令牌,但此令牌包含空用户名(因为此时用户是匿名的)
- 您正在使用AJAX调用登录
- 用户将注释表单提交给服务器,并且令牌validation失败,因为初始令牌中包含的空用户名与当前经过身份validation的用户名不同。
因此,您有几个选项可以解决此问题:
- 在步骤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
:
@{ 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()
上有一篇很棒的文章 ,如果你想了解更多如何使用它以及它的用途。