AntiForgeryToken 值属于页面而不是form。如果您将多个表单(当然不是嵌套的!!!)与@Html.AntiForgeryToken() 放在同一页面上,那么所有值都将相同。当页面重新加载时(在GET 或POST 之后),值会发生变化。
在内部,Web 服务器设置 HttpOnly Cookie(在 AntiForgeryConfig 类中定义的名称)并将接收到的值与该 cookie 值进行比较。
如果您从该页面进行 API (AJAX) 调用会怎样?当然,您可以拨打任意数量的电话。
如果页面上没有form怎么办?您需要添加 fake form。像这样。
@{
var attr = new Dictionary<string, object>();
attr.Add("id", "anti-forgery"); //sic!
}
@using (Html.BeginForm("", "fake-form", FormMethod.Post, attr))
{
@Html.AntiForgeryToken()
}
然后立即设置所有您的 AJAX 调用。
<script>
'use strict';
$(document).ready(function () {
$.ajaxSetup({
dataType: 'json',
method: 'POST',
contentType: 'application/json',
headers: antiForgery({}) //put antiforgerytoken into ajax request header
});
function antiForgery(data) {
data.__RequestVerificationToken = $('#anti-forgery input[name=__RequestVerificationToken]').val();
return data;
}
});
</script>
在服务器端,您需要使用并验证请求。以下方法使用自定义属性类。
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
public class ValidateJsonAntiForgeryTokenAttribute : FilterAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext == null)
{
throw new ArgumentNullException("filterContext");
}
var httpContext = filterContext.HttpContext;
var cookie = httpContext.Request.Cookies[AntiForgeryConfig.CookieName];
AntiForgery.Validate(cookie != null ? cookie.Value : null, httpContext.Request.Headers["__RequestVerificationToken"]);
}
}
现在你只用这个属性装饰你的控制器方法。
[HttpPost]
[ValidateJsonAntiForgeryToken] //this one
public async Task<JsonResult> ProcessRq(MyModel model)
{
//do work
}
如果您创建纯 API (RESTfull) 服务,则有类似的方法。客户端应用首先必须请求和接收某种身份验证令牌,并将其添加到所有下一个请求中(在会话期间)。