我需要在同一页面中的另一个视图中成功登录后,在位于视图中的表单中重新加载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,否则我会收到验证错误,因为现在登录已经不同了。
由于
答案 0 :(得分:21)
问题正在发生,因为AntiForgery令牌包含当前经过身份验证的用户的用户名。
所以这就是发生的事情:
所以你有几个方法可以解决这个问题:
解决方案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。