我有一个布局页面,其中包含带有AntiForgeryToken的表单
using (Html.BeginForm(action, "Account", new { ReturnUrl = returnUrl }, FormMethod.Post, new { Id = "xcrf-form" }))
这将生成一个隐藏字段
<input name="__RequestVerificationToken" type="hidden" value="p43bTJU6xjctQ-ETI7T0e_0lJX4UsbTz_IUjQjWddsu29Nx_UE5rcdOONiDhFcdjan88ngBe5_ZQbHTBieB2vVXgNJGNmfQpOm5ATPbifYE1">
在我的角度视图中(在布局页面的div 中加载,我这样做
<form class="form" role="form" ng-submit="postReview()">
我的 postReview() 代码如下
$scope.postReview = function () {
var token = $('[name=__RequestVerificationToken]').val();
var config = {
headers: {
"Content-Type": "multipart/form-data",
// the following when uncommented does not work either
//'RequestVerificationToken' : token
//"X-XSRF-TOKEN" : token
}
}
// tried the following, since my other MVC controllers (non-angular) send the token as part of form data, this did not work though
$scope.reviewModel.__RequestVerificationToken = token;
// the following was mentioned in some link I found, this does not work either
$http.defaults.headers.common['__RequestVerificationToken'] = token;
$http.post('/Review/Create', $scope.reviewModel, config)
.then(function (result) {
// Success
alert(result.data);
}, function (error) {
// Failure
alert("Failed");
});
}
我的MVC创建方法如下
[HttpPost]
[ValidateAntiForgeryToken]
[AllowAnonymous]
public ActionResult Create([Bind(Include = "Id,CommentText,Vote")] ReviewModel reviewModel)
{
if (User.Identity.IsAuthenticated == false)
{
// I am doing this instead of [Authorize] because I dont want 302, which browser handles and I cant do client re-direction
return new HttpStatusCodeResult(HttpStatusCode.Forbidden);
}
// just for experimenting I have not yet added it to db, and simply returning
return new JsonResult {Data = reviewModel, JsonRequestBehavior = JsonRequestBehavior.AllowGet};
}
因此,无论我把令牌放在哪里,无论我用什么来表示"内容类型"(我尝试了application-json和www-form-urlencoded),我总是收到错误"所需的防伪表单字段"__RequestVerificationToken"不存在。
我甚至尝试命名__RequestVerificationToken和RequestVerificationToken
。为什么我的服务器找不到该死的令牌?
我还查看了几个链接,这些链接要求您实现自己的 AntiForgeryToeknVerifyAttrbute 并验证作为 cookieToken:formToken 发送的令牌,我没有尝试过,但为什么我无法让它工作,而这适用于 MVC 控制器(非角度帖子)
是的。默认情况下,MVC 框架将检查Request.Form["__RequestVerificationToken"]
。
检查 MVC 源代码
public AntiForgeryToken GetFormToken(HttpContextBase httpContext)
{
string value = httpContext.Request.Form[_config.FormFieldName];
if (String.IsNullOrEmpty(value))
{
// did not exist
return null;
}
return _serializer.Deserialize(value);
}
您需要创建自己的过滤器才能从Request.Header
进行检查
Phil Haack 文章中的代码片段 - MVC 3
private class JsonAntiForgeryHttpContextWrapper : HttpContextWrapper {
readonly HttpRequestBase _request;
public JsonAntiForgeryHttpContextWrapper(HttpContext httpContext)
: base(httpContext) {
_request = new JsonAntiForgeryHttpRequestWrapper(httpContext.Request);
}
public override HttpRequestBase Request {
get {
return _request;
}
}
}
private class JsonAntiForgeryHttpRequestWrapper : HttpRequestWrapper {
readonly NameValueCollection _form;
public JsonAntiForgeryHttpRequestWrapper(HttpRequest request)
: base(request) {
_form = new NameValueCollection(request.Form);
if (request.Headers["__RequestVerificationToken"] != null) {
_form["__RequestVerificationToken"]
= request.Headers["__RequestVerificationToken"];
}
}
public override NameValueCollection Form {
get {
return _form;
}
}
}
[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 = new JsonAntiForgeryHttpContextWrapper(HttpContext.Current);
AntiForgery.Validate(httpContext, Salt ?? string.Empty);
}
public string Salt {
get;
set;
}
// The private context classes go here
}
在此处查看 MVC 4 实现,以避免salt
问题
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class,
AllowMultiple = false, Inherited = true)]
public sealed 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"]);
}
}
我遇到了同样的问题。事实证明,我不需要在我的角度js代码中明确设置防伪令牌。MVC 控制器期望从 1 传递此令牌值。表单域,2。饼干。过滤器等同于并快乐地匹配。当我们提交表单时,防伪令牌的隐藏字段会自动提供其值。Cookie 由浏览器自动设置。所以正如我所说,我们不需要明确地做任何事情。
问题实际上是请求的内容类型。默认情况下,它作为应用程序/json,因此不会收到a.f.令牌值(或者更确切地说是任何表单数据)。以下对我有用:
// create the controller
var RegisterController = function ($scope, $http) {
$scope.onSubmit = function (e) {
// suppress default form submission
e.preventDefault();
var form = $("#registerform");
if (form.valid()) {
var url = form.attr('action');
var data = form.serialize();
var config = {
headers: {
'Content-type':'application/x-www-form-urlencoded',
}
};
$http.post(url, data, config).success(function (data) {
alert(data);
}).error(function(reason) {
alert(reason);
});
}
};
};
正如穆拉利建议的那样,我想我需要将 toekn 放在表单本身中,所以我尝试将令牌作为表单数据的一部分,我需要按照 https://stackoverflow.com/a/14868725/2475810 中所述对表单数据进行编码
这种方法不需要在服务器端进行任何额外的代码,我们也不需要创建和加入cookie和表单令牌。只需对数据进行形式编码并将 token 作为字段之一,如上面的答案中所述,我们就可以让它滚动。
您应该以这种方式执行 HTTP 请求:
$http({
url: '/Review/Create',
data: "__RequestVerificationToken=" + token + "¶m1=1¶m2=2",
method: 'POST',
headers: {
'X-Requested-With': 'XMLHttpRequest',
'Accept': 'application/json, text/javascript, */*; q=0.01',
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
}
}).success(function(result) {
alert(result.data);
}).error(function(error) {
alert("Failed");
});