登录后重新加载AntiForgeryToken

时间:2013-05-29 13:45:47

标签: jquery asp.net-mvc razor security

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

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

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

我该怎么做?

编辑:

在布局中我有不同的PartialViews:

登录的部分:

<ul class="menudrt" id="headerLogin">
    @{ Html.RenderAction(MVC.Account.LoginHeader()); }
</ul>

在另一部分中,发表评论的可能性很小:

<div class="comentsform">

    <!-- Comments form -->
    @{ Html.RenderAction(MVC.Comment.Create()); }

</div>

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

由于

2 个答案:

答案 0 :(得分:21)

问题正在发生,因为AntiForgery令牌包含当前经过身份验证的用户的用户名。

所以这就是发生的事情:

  1. 匿名用户导航到您的网页
  2. 为评论表单生成防伪令牌,但此令牌包含空用户名(因为此时用户是匿名的)
  3. 您正在使用AJAX调用登录
  4. 用户将评论表单提交给服务器,并且令牌验证失败,因为初始令牌中包含的空用户名与当前经过身份验证的用户名不同。
  5. 所以你有几个方法可以解决这个问题:

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

答案 1 :(得分:6)

您只需在登录后返回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进行验证。

如果您想了解有关如何使用它以及它的用途的更多信息,请AntiForgeryToken()上的{p> Steven Sanderson has a great post