【问题标题】:How to make ajax request with anti-forgery token in mvc如何在 mvc 中使用防伪令牌发出 ajax 请求
【发布时间】:2013-11-16 07:44:39
【问题描述】:

我对 MVC 项目的以下详细信息有疑问。

当我尝试将 jquery ajax 请求与旋转 gif(甚至文本)等加载面板一起使用时,我收到了错误,从提琴手观察到

所需的防伪表单字段“__RequestVerificationToken”不存在。

如果我评论 [ValidateAntiForgeryToken] attribute 在 POST 操作方法中使用加载面板工作正常。我想知道为什么会出现此错误。

我什至用过序列化的查询字符串

__RequestVerificationToken= $('input[name="__RequestVerificationToken"').val()

还是报错

无法解密防伪令牌。如果此应用程序由 Web Farm 或群集托管,请确保所有计算机都运行相同版本的 ASP.NET 网页,并且 <machineKey> 配置指定显式​​加密和验证密钥。

AutoGenerate 不能在集群中使用

我应该使用什么?

这里更新了问题代码

var token = $('input[name="__RequestVerificationToken"]').val();
$('#submitaddress').click(function subaddr(event) {
    event.preventDefault();
    event.stopPropagation();
  //$('#addAddress').html('<img src="/img/animated-overlay.gif"> Sending...');
   // $('#addAddress').blur();
    //  $(this).bl
    if ($('#Jobid').val()!="") {
        $('#TransportJobId').val(parseInt($('#Jobid').val()));
        $.ajax(
              {
                  url: '/TransportJobAddress/create',
                  type: 'POST',
                  data: "__RequestVerificationToken=" + token + "" + $('form[action="/TransportJobAddress/Create"]').serialize(),
                  success: function poste(data, textStatus, jqXHR) { $('#addAddress').html(data); return false; },
                  error: function err(jqXHR, textStatus, errorThrown) { alert('error at address :' + errorThrown); }
              });
    }
    else {
        var transportid = 2;
        $.ajax({
            url: '/TransportJob/create',
            type: 'POST',
            data: "__RequestVerificationToken=" + token + "" + $('form[action="/TransportJob/Create"]').serialize(),
            success: function sfn(data, textStatus, jqXHR) {
                transportid = parseInt(data);
                $('#Jobid').val(data);
               // alert('inserted id :' + data);
                $('#TransportJobId').val((transportid));
                $.ajax(
         {

             url: '/TransportJobAddress/create',
             type: 'POST',
             //beforeSend: function myintserver(xhr){  
             //        $('#addAddress').html('<div id="temp_load" style="text-align:center">please wait ...</div>');
             //}, 
             data: "__RequestVerificationToken=" + token + "" + $('form[action="/TransportJobAddress/Create"]').serialize(),
             success: function poste(data, textStatus, jqXHR) {
                 $('#addAddress').html(data);

             },
             error: function err(jqXHR, textStatus, errorThrown) {
                 alert('error at address :' + errorThrown);
             }

         });
            },
            error: function myfunction(jqXHR, textStatus, errorThrown) {
                alert("error at transport :" + jqXHR.textStatus);
            },
            complete: function completefunc() {
              //  alert('ajax completed all requests');
                return false;
            }

        });
    }
});

表单标签

<form action="/TransportJob/Create" method="post"><input     name="__RequestVerificationToken" type="hidden"   value="ydYSei0_RfyBf619dQrhDwwoCM7OwWkJQQEMNvNdAkefiFfYvRQ0MJYYu0zkktNxlJk_y1ZJO9-yb-  COap8mqd0cvh8cDYYik4HJ0pZXTgE1" />   

运输工作 在同一页面上形成标签 2

<form action="/TransportJobAddress/Create" method="post" novalidate="novalidate"><input name="__RequestVerificationToken" type="hidden"    value="Np2vUZJPk1TJlv846oPSU6hg4SjMHRcCk1CacaqZbpHOg8WbV4GZv06noRDl7F_iT9qQf3BIXo3n9wGW68sU mki7g3-ku_BSHBDN-g2aaKc1"> 

【问题讨论】:

  • 您是否已将令牌添加到您的视图中?像这样:@Html.AntiForgeryToken()
  • 默认在form标签后的fieldset标签前添加
  • 将您的 AntiForgeryToken 添加到 ajax 调用的标头中,而不是在数据中
  • 刚刚看到你的更新。您能否向我们展示您的 ajax 请求中的数据 + 标头字段?
  • 我自己解决了这个问题,因为我的错误是,我在 ajax 收集详细信息之前使用了加载面板 id(不应该在按钮单击事件下使用加载面板,我们应该在发送 ajax 事件之前使用)除了我的错误解决方案是由@freshbm

标签: jquery ajax asp.net-mvc asp.net-mvc-4


【解决方案1】:

视图或布局:

@html.antiforgeryToken();

ajax调用函数:

var data={...};
var token=$('#_id').serializeObject();
var dataWithAntiforgeryToken = $.extend(data,token);

$.ajax(
{
....,
data: dataWithAntiforgeryToken;
}
)

【讨论】:

    【解决方案2】:

    我想保护 Ajax 和正常请求,所以我想出了以下内容:

    首先使用来自haacked.com 的优秀博客,我按照描述创建了 ConditionalFilterProvider。

    然后我按照codethinked 的博客中的描述创建了所有类。

    在我的 _layout 页面上,我按照博客中的描述添加了带有 $.ajaxPrefilter 的片段...这可以确保我的所有 Ajax 回调现在都通过标头发送 Antiforgery 令牌。

    为了将所有内容粘合在一起,我在 global.asax / Application_Start 上添加了这段代码

    (c, a) =>
                        (c.HttpContext.Request.IsAjaxRequest() &&
                         !string.Equals(c.HttpContext.Request.HttpMethod, "GET"))
                            ? new AjaxValidateAntiForgeryTokenAttribute()
                            : null,
    (c, a) =>
                        (!c.HttpContext.Request.IsAjaxRequest() &&
                         string.Equals(c.HttpContext.Request.HttpMethod, "POST", StringComparison.OrdinalIgnoreCase))
                            ? new ValidateAntiForgeryTokenAttribute()
                            : null
    

    基本上.. 将属性注入到我所有不是 GET 的控制器中。

    之后,我只需要转到所有(很少)表格并添加 @Html.AntiForgeryToken()。

    为了证明一切正常,我只是尝试使用没有 AntiForgeryToken 的表单来填充事物并获得预期的异常。 并删除 $.ajaxPrefilter 并创建 Ajax 请求并收到预期的异常。

    【讨论】:

    • 感谢您的建议。这样更好吗?
    • 是的 - 感谢您为本网站的质量做出贡献 :-)
    • 这意味着每个可以模拟 ajax 调用的人(即任何拥有 JavaScript 调试器的人)都可以在您的服务器上执行调用而无需伪造令牌。
    【解决方案3】:

    您是否已将令牌添加到 ajax 调用的标头中?

    您需要在 ajax 调用的消息头中添加 AntiForgeryToken:

    var token = $('input[name="__RequestVerificationToken"]').val();
    
    var headers = {};
    
    headers['__RequestVerificationToken'] = token;
    
    $.ajax({
            url: ... some url,
            headers: headers,
            ....
    });
    

    在你的代码中试试这个:

    var token = $('input[name="__RequestVerificationToken"]').val();
    var tokenadr = $('form[action="/TransportJobAddress/Create"] input[name="__RequestVerificationToken"]').val(); 
    
    var headers = {};
    var headersadr = {};
    headers['__RequestVerificationToken'] = token;
    headersadr['__RequestVerificationToken'] = tokenadr;
    
    $('#submitaddress').click(function subaddr(event) {
        event.preventDefault();
        event.stopPropagation();
      //$('#addAddress').html('<img src="/img/animated-overlay.gif"> Sending...');
       // $('#addAddress').blur();
        //  $(this).bl
        if ($('#Jobid').val()!="") {
            $('#TransportJobId').val(parseInt($('#Jobid').val()));
            $.ajax(
                  {
                      url: '/TransportJobAddress/create',
                      type: 'POST',
                      headers:headersadr, 
                      data: "__RequestVerificationToken=" + token + "" + $('form[action="/TransportJobAddress/Create"]').serialize(),
                      success: function poste(data, textStatus, jqXHR) { $('#addAddress').html(data); return false; },
                      error: function err(jqXHR, textStatus, errorThrown) { alert('error at address :' + errorThrown); }
                  });
        }
        else {
            var transportid = 2;
            $.ajax({
                url: '/TransportJob/create',
                type: 'POST',
                headers:headers, 
                data: $('form[action="/TransportJob/Create"]').serialize(),
                success: function sfn(data, textStatus, jqXHR) {
                    transportid = parseInt(data);
                    $('#Jobid').val(data);
                   // alert('inserted id :' + data);
                    $('#TransportJobId').val((transportid));
                    $.ajax(
             {
    
                 url: '/TransportJobAddress/create',
                 type: 'POST',
                 //beforeSend: function myintserver(xhr){  
                 //        $('#addAddress').html('<div id="temp_load" style="text-align:center">please wait ...</div>');
                 //},
                 headers:headers, 
                 data: $('form[action="/TransportJobAddress/Create"]').serialize(),
                 success: function poste(data, textStatus, jqXHR) {
                     $('#addAddress').html(data);
    
                 },
                 error: function err(jqXHR, textStatus, errorThrown) {
                     alert('error at address :' + errorThrown);
                 }
    
             });
                },
                error: function myfunction(jqXHR, textStatus, errorThrown) {
                    alert("error at transport :" + jqXHR.textStatus);
                },
                complete: function completefunc() {
                  //  alert('ajax completed all requests');
                    return false;
                }
    
            });
        }
    });
    

    在您的 ajax 调用中添加了标题行。

    【讨论】:

    • 嗨freshbm,将标题添加到第一个表单有效,但是在提交第二个时它不起作用如何将标题添加到第二个表单也不同于第一个表单?我使用了不同的标头,例如 var tokenaddr = $('form[action="/TransportJobAddress/create"] input[name="__RequestVerificationToken"]').val(); headers['__RequestVerificationToken'] = tokenaddr;
    • 我在同一页面上添加了两个表单标签如何获取两个令牌并在标题中使用
    • 也许您拼错了表单操作:$('form[action="/TransportJobAddress/Create"] 在您的评论中创建是小写字母。tokenaddr 的值是什么?
    • 如果我使用 var tokenaddr =('form[action="/TransportJobAddress/create"] input[name="__RequestVerificationToken"]').val();标头['__RequestVerificationToken'] = tokenaddr;上面作为提琴手中的第二种形式标头得到响应 RequestVerificationToken 不存在。不知道我如何错过第二种形式的标头。
    【解决方案4】:

    您是否已将令牌添加到您的视图中?像这样:

    <form method="post" action="/my-controller/my-action">
        @Html.AntiForgeryToken()
    </form>
    

    由于您接收帖子的控制器正在寻找防伪令牌,因此您需要确保将其添加到视图中的表单中。

    编辑:

    先尝试在 json 中构建您的数据:

    var formData = $('form[action="/TransportJobAddress/Create"]').serialize();
    $.extend(formData, {'__RequestVerificationToken': token });
    
    //and then in your ajax call:
    $.ajax({
        //...
        data:formData
        //...
    });
    

    【讨论】:

    • @using (Html.BeginForm()) @Html.AntiForgeryToken() @Html.ValidationSummary(true) 默认情况下这些都在那里,我将它与 ajax 请求一起使用。
    • 但是您正在通过 ajax 提交表单。您是否确保该字段值包含在提交的 ajax 表单数据中?
    • 您可以使用 fiddler 或 firebug 查看实际发布的内容吗?或者将执行 ajax 调用的代码发布到上述问题。
    • 是的令牌与查询字符串一起提交,并从提琴手观察到
    • 您使用的是网络农场还是集群?
    【解决方案5】:

    我通常不会手动将其添加到每个请求中,而是这样做:

    var token = $('input[name="__RequestVerificationToken"]').val();
    $.ajaxPrefilter(function (options, originalOptions) {
      if (options.type.toUpperCase() == "POST") {
        options.data = $.param($.extend(originalOptions.data, { __RequestVerificationToken: token }));
      }
    });
    

    这会自动将您的令牌添加到您所做的任何 ajax POST。

    【讨论】:

    • 很好的解决方法!只是一个小评论:如果您用作 ajax 调用的“数据”是 FormData(如@hofnarwillie 答案中所示),则必须使用 .append:options.data.append("__RequestVerificationToken", token);
    猜你喜欢
    • 2023-04-09
    • 2016-10-25
    • 2015-01-11
    • 1970-01-01
    • 2015-05-11
    • 2014-10-03
    • 2011-05-17
    • 1970-01-01
    • 2020-06-17
    相关资源
    最近更新 更多