我正在使用以下帖子的代码:
首先,我将使用控制器操作的正确值填充数组变量。
使用下面的代码我认为只需在JavaScript代码中添加以下代码就可以非常直接:
data["__RequestVerificationToken"] = $('[name=__RequestVerificationToken]').val();
<%= Html.AntiForgeryToken() %>
位于正确的位置,操作有[ValidateAntiForgeryToken]
但我的控制器动作一直说:“无效的伪造令牌”
我在这里做错了什么?
data["fiscalyear"] = fiscalyear;
data["subgeography"] = $(list).parent().find('input[name=subGeography]').val();
data["territories"] = new Array();
$(items).each(function() {
data["territories"].push($(this).find('input[name=territory]').val());
});
if (url != null) {
$.ajax(
{
dataType: 'JSON',
contentType: 'application/json; charset=utf-8',
url: url,
type: 'POST',
context: document.body,
data: JSON.stringify(data),
success: function() { refresh(); }
});
}
答案 0 :(得分:56)
自MVC 4以来,您不需要ValidationHttpRequestWrapper解决方案。根据此link。
这是我的解决方案:
var token = $('input[name="__RequestVerificationToken"]').val();
var headers = {};
headers['__RequestVerificationToken'] = token;
$.ajax({
type: 'POST',
url: '/MyTestMethod',
contentType: 'application/json; charset=utf-8',
headers: headers,
data: JSON.stringify({
Test: 'test'
}),
dataType: "json",
success: function () {},
error: function (xhr) {}
});
[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]
[AllowAnonymous]
[ValidateJsonAntiForgeryToken]
public async Task<JsonResult> MyTestMethod(string Test)
{
return Json(true);
}
答案 1 :(得分:46)
错误的是,应该处理此请求的控制器操作以及[ValidateAntiForgeryToken]
标记的控制器操作需要将一个名为__RequestVerificationToken
的参数与请求一起发布。
没有这样的参数POST,因为你使用JSON.stringify(data)
将表单转换为JSON表示,因此抛出了异常。
所以我可以在这里看到两种可能的解决方案:
数字1:使用x-www-form-urlencoded
代替JSON
发送您的请求参数:
data["__RequestVerificationToken"] = $('[name=__RequestVerificationToken]').val();
data["fiscalyear"] = fiscalyear;
// ... other data if necessary
$.ajax({
url: url,
type: 'POST',
context: document.body,
data: data,
success: function() { refresh(); }
});
数字2:将请求分成两个参数:
data["fiscalyear"] = fiscalyear;
// ... other data if necessary
var token = $('[name=__RequestVerificationToken]').val();
$.ajax({
url: url,
type: 'POST',
context: document.body,
data: { __RequestVerificationToken: token, jsonRequest: JSON.stringify(data) },
success: function() { refresh(); }
});
因此,在所有情况下,您都需要发布__RequestVerificationToken
值。
答案 2 :(得分:11)
我刚刚在我当前的项目中实现了这个实际问题。我为所有需要经过身份验证的用户的Ajax POST做了这个。
首先,我决定挂钩我的jQuery Ajax调用,所以我不要经常重复自己。此JavaScript代码段确保所有ajax(post)调用都将我的请求验证令牌添加到请求中。注意:.NET框架使用名称__RequestVerificationToken,因此我可以使用标准的Anti-CSRF功能,如下所示。
$(document).ready(function () {
securityToken = $('[name=__RequestVerificationToken]').val();
$('body').bind('ajaxSend', function (elm, xhr, s) {
if (s.type == 'POST' && typeof securityToken != 'undefined') {
if (s.data.length > 0) {
s.data += "&__RequestVerificationToken=" + encodeURIComponent(securityToken);
}
else {
s.data = "__RequestVerificationToken=" + encodeURIComponent(securityToken);
}
}
});
});
在您需要令牌可用于上述JavaScript代码的视图中,只需使用常见的HTML-Helper即可。您基本上可以在任何地方添加此代码。我将它放在if(Request.IsAuthenticated)语句中:
@Html.AntiForgeryToken() // You can provide a string as salt when needed which needs to match the one on the controller
在您的控制器中,只需使用标准的ASP.NET MVC反CSRF机制。我是这样做的(尽管我实际上使用了盐)。
[HttpPost]
[Authorize]
[ValidateAntiForgeryToken]
public JsonResult SomeMethod(string param)
{
// Do something
return Json(true);
}
使用Firebug或类似工具,您可以轻松查看POST请求现在如何附加__RequestVerificationToken参数。
答案 3 :(得分:7)
您可以设置$.ajax的traditional
属性并将其设置为true
,以便以网址编码形式发送json数据。请务必设置type:'POST'
。使用此方法,您甚至可以发送数组,而不必使用JSON.stringyfy或服务器端的任何更改(例如,创建自定义属性来嗅探头)
我在ASP.NET MVC3和jquery 1.7设置上试过这个并且它正在工作
以下是代码段。
var data = { items: [1, 2, 3], someflag: true};
data.__RequestVerificationToken = $(':input[name="__RequestVerificationToken"]').val();
$.ajax({
url: 'Test/FakeAction'
type: 'POST',
data: data
dataType: 'json',
traditional: true,
success: function (data, status, jqxhr) {
// some code after succes
},
error: function () {
// alert the error
}
});
这将与具有以下签名的MVC操作相匹配
[HttpPost]
[Authorize]
[ValidateAntiForgeryToken]
public ActionResult FakeAction(int[] items, bool someflag)
{
}
答案 4 :(得分:5)
我在我的JSON对象中持有令牌,当帖子为json时,我最终修改了ValidateAntiForgeryToken类以检查InputStream对象的Request。我已经写了一篇关于它的blog post,希望你会觉得它很有用。
答案 5 :(得分:5)
您无法验证contentType类型的内容:'application / json; charset = utf-8'因为你的日期不会上传到请求的 Form 属性,而是在InputStream属性中,你将永远不会有这个Request.Form [“__ RequestVerificationToken”]。 / p>
这将始终为空,验证将失败。
答案 6 :(得分:5)
当您收到发布的JSON时,您将不必验证AntiForgeryToken。
原因是已创建AntiForgeryToken以防止CSRF。由于您无法将AJAX数据发布到其他主机,并且HTML表单无法提交JSON作为请求正文,因此您无需保护您的应用程序免受已发布的JSON的攻击。</ p>
答案 7 :(得分:3)
我已使用RequestHeader全局解决了这个问题。
var DisciplinesCollection = Backbone.Collection.extend({
model: Discipline,
filterByCategory: function(cat){
return _(this.filter(function(data) {
return _.contains(data.get("category"), cat);
}));
}
其中requestVerificationTokenVariable是包含标记值的变量字符串。 然后所有ajax调用都将令牌发送到服务器,但默认的ValidateAntiForgeryTokenAttribute获取Request.Form值。 我已经写了并添加了这个globalFilter,它将令牌从header复制到request.form,而不是我可以使用默认的ValidateAntiForgeryTokenAttribute:
<?php
//simulating your array
$x = new StdClass();
$x->task_id = 1;
$y = new StdClass();
$y->task_id = 28;
$z = new StdClass();
$z->task_id = 43;
$object = [
[0 => $x],
[
1 => $y,
2 => $z
]];
//converting the objects to array
$array = json_decode(json_encode($object), true);
$new_array = [];
//implode
foreach($array as $k1 => $a){
$new_array[$k1] = implode(', ', array_column($a, 'task_id'));
}
var_dump($new_array);
这项工作对我来说:)
答案 8 :(得分:2)
查看Dixin's Blog以获取有关这方面的精彩帖子。
另外,为什么不使用$ .post而不是$ .ajax?
随着该页面上的jQuery插件,您可以执行以下简单的操作:
data = $.appendAntiForgeryToken(data,null);
$.post(url, data, function() { refresh(); }, "json");
答案 9 :(得分:1)
在发布JSON时,我必须要有点阴暗才能验证防伪令牌,但它确实有效。
//If it's not a GET, and the data they're sending is a string (since we already had a separate solution in place for form-encoded data), then add the verification token to the URL, if it's not already there.
$.ajaxSetup({
beforeSend: function (xhr, options) {
if (options.type && options.type.toLowerCase() !== 'get' && typeof (options.data) === 'string' && options.url.indexOf("?__RequestVerificationToken=") < 0 && options.url.indexOf("&__RequestVerificationToken=") < 0) {
if (options.url.indexOf('?') < 0) {
options.url += '?';
}
else {
options.url += '&';
}
options.url += "__RequestVerificationToken=" + encodeURIComponent($('input[name=__RequestVerificationToken]').val());
}
}
});
但是,正如一些人已经提到的,验证只检查表单 - 而不是JSON,而不是查询字符串。所以,我们超越了属性的行为。重新实现所有验证将是非常糟糕的(并且可能不安全),所以我只是覆盖Form属性,如果令牌在QueryString中传递,则具有内置验证THINK它在表单中。 / p>
这有点棘手,因为表单是只读的,但可行。
if (IsAuth(HttpContext.Current) && !IsGet(HttpContext.Current))
{
//if the token is in the params but not the form, we sneak in our own HttpContext/HttpRequest
if (HttpContext.Current.Request.Params != null && HttpContext.Current.Request.Form != null
&& HttpContext.Current.Request.Params["__RequestVerificationToken"] != null && HttpContext.Current.Request.Form["__RequestVerificationToken"] == null)
{
AntiForgery.Validate(new ValidationHttpContextWrapper(HttpContext.Current), null);
}
else
{
AntiForgery.Validate(new HttpContextWrapper(HttpContext.Current), null);
}
}
//don't validate un-authenticated requests; anyone could do it, anyway
private static bool IsAuth(HttpContext context)
{
return context.User != null && context.User.Identity != null && !string.IsNullOrEmpty(context.User.Identity.Name);
}
//only validate posts because that's what CSRF is for
private static bool IsGet(HttpContext context)
{
return context.Request.HttpMethod.ToUpper() == "GET";
}
...
internal class ValidationHttpContextWrapper : HttpContextBase
{
private HttpContext _context;
private ValidationHttpRequestWrapper _request;
public ValidationHttpContextWrapper(HttpContext context)
: base()
{
_context = context;
_request = new ValidationHttpRequestWrapper(context.Request);
}
public override HttpRequestBase Request { get { return _request; } }
public override IPrincipal User
{
get { return _context.User; }
set { _context.User = value; }
}
}
internal class ValidationHttpRequestWrapper : HttpRequestBase
{
private HttpRequest _request;
private System.Collections.Specialized.NameValueCollection _form;
public ValidationHttpRequestWrapper(HttpRequest request)
: base()
{
_request = request;
_form = new System.Collections.Specialized.NameValueCollection(request.Form);
_form.Add("__RequestVerificationToken", request.Params["__RequestVerificationToken"]);
}
public override System.Collections.Specialized.NameValueCollection Form { get { return _form; } }
public override string ApplicationPath { get { return _request.ApplicationPath; } }
public override HttpCookieCollection Cookies { get { return _request.Cookies; } }
}
还有其他一些与我们的解决方案不同的东西(具体来说,我们使用HttpModule,因此我们不必将属性添加到每个POST),而我为了简洁而省略了。如有必要,我可以添加它。
答案 10 :(得分:1)
使用Newtonsoft.JSON库可以使使用AntiForgerytoken进行基于AJAX的模型发布变得更加容易
以下方法对我有用:
保持这样的AJAX帖子:
$.ajax(
{
dataType: 'JSON',
url: url,
type: 'POST',
context: document.body,
data: {
'__RequestVerificationToken' : token,
'model_json': JSON.stringify(data)
}; ,
success: function() { refresh(); }
});
然后在您的MVC操作中:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(FormCollection data)
{
var model= JsonConvert.DeserializeObject<Order>(data["model_json"]);
return Json(1);
}
希望这会有所帮助:)
答案 11 :(得分:0)
对我来说不幸的是,其他答案依赖于jquery处理的一些请求格式,并且在直接设置有效负载时它们都没有用。 (公平地说,把它放在标题中会起作用,但我不想走那条路。)
要在beforeSend
功能中完成此操作,以下操作。 $.params()
将对象转换为标准格式/ url编码格式。
我曾尝试过使用令牌对json进行字符串化的各种变体,但没有一个能够正常工作。
$.ajax({
...other params...,
beforeSend: function(jqXHR, settings){
var token = ''; //get token
data = {
'__RequestVerificationToken' : token,
'otherData': 'value'
};
settings.data = $.param(data);
}
});
```
答案 12 :(得分:-1)
您应该将AntiForgeryToken放在表单标记中:
@using (Html.BeginForm(actionName:"", controllerName:"",routeValues:null, method: FormMethod.Get, htmlAttributes: new { @class="form-validator" }))
{
@Html.AntiForgeryToken();
}
然后在javascript中修改以下代码
var DataToSend = [];
DataToSend.push(JSON.stringify(data),$('form.form-validator').serialize());
$.ajax(
{
dataType: 'JSON',
contentType: 'application/json; charset=utf-8',
url: url,
type: 'POST',
context: document.body,
data: DataToSend,
success: function() { refresh(); }
});
然后您应该能够使用ActionResult注释验证请求
[ValidateAntiForgeryToken]
[HttpPost]
我希望这会有所帮助。