【问题标题】:WARNING: Can't verify CSRF token authenticity rails警告:无法验证 CSRF 令牌真实性轨道
【发布时间】:2011-11-04 10:07:38
【问题描述】:

我正在使用 AJAX 将数据从视图发送到控制器并且出现此错误:

警告:无法验证 CSRF 令牌的真实性

我想我必须用数据发送这个令牌。

有谁知道我该怎么做?

编辑:我的解决方案

我通过将以下代码放入 AJAX 帖子中来做到这一点:

headers: {
  'X-Transaction': 'POST Example',
  'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')
},

【问题讨论】:

  • 您的布局标题中有 吗?
  • 是这样的:
  • 你有提供 ajax 客户端功能的 jquery-rails 库吗?
  • 而HAML的方式是加上“= csrf_meta_tags”
  • 好问题,感谢提问

标签: ruby-on-rails jquery csrf


【解决方案1】:

你应该这样做:

  1. 确保您的布局中有<%= csrf_meta_tag %>

  2. beforeSend添加到所有ajax请求中以设置如下标头:


$.ajax({ url: 'YOUR URL HERE',
  type: 'POST',
  beforeSend: function(xhr) {xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'))},
  data: 'someData=' + someData,
  success: function(response) {
    $('#someDiv').html(response);
  }
});

要在您可以使用的所有请求中发送令牌:

$.ajaxSetup({
  headers: {
    'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')
  }
});

【讨论】:

  • 谢谢!像魅力一样为我工作!
  • Rails 团队提供的 jQuery UJS 库自动将 CSRF 令牌添加到 jQuery AJAX 请求中。自述文件包含有关如何进行设置的说明。 github.com/rails/jquery-ujs/blob/master/src/rails.js#L91
  • 请注意,您可以使用 $.ajaxSetup 函数一次性设置所有请求的标头。
  • 太棒了!一直在寻找这个答案。无缝工作。谢谢!
  • 请注意,如果您按照上面的建议使用 jQuery UJS,您需要确保 rails-ujs 包含在 jquery 包含之后,否则它将失败与操作相同的错误。
【解决方案2】:

做到这一点的最佳方法实际上是使用<%= form_authenticity_token.to_s %> 直接在您的rails 代码中打印出令牌。您不需要像其他帖子提到的那样使用 javascript 来搜索 dom 以获取 csrf 令牌。只需添加 headers 选项,如下所示;

$.ajax({
  type: 'post',
  data: $(this).sortable('serialize'),
  headers: {
    'X-CSRF-Token': '<%= form_authenticity_token.to_s %>'
  },
  complete: function(request){},
  url: "<%= sort_widget_images_path(@widget) %>"
})

【讨论】:

  • 您可以将 headers 添加到 $.ajaxSetup(),而不是为每个 ajax 命令执行此操作。
  • 我宁愿推荐使用this answer...
  • 我不太喜欢在 javascript 中使用 ERB 的方法。
  • 这会迫使您使用 ERB 生成 javascript,这是非常有限的。即使有些地方 ERB 可能很适合,但也有其他地方不适合,并且仅仅为了获得代币而添加它是一种浪费。
【解决方案3】:

如果我没记错的话,您必须将以下代码添加到您的表单中,以摆脱这个问题:

<%= token_tag(nil) %>

不要忘记参数。

【讨论】:

  • 其实应该是:&lt;%= token_tag(nil) %&gt;。然后你会得到自动生成的令牌。
【解决方案4】:

确实是最简单的方法。不要为更改标题而烦恼。

确保你有:

<%= csrf_meta_tag %> in your layouts/application.html.erb

只需像这样做一个隐藏的输入字段:

<input name="authenticity_token" 
               type="hidden" 
               value="<%= form_authenticity_token %>"/>

或者如果你想要一个 jQuery ajax 帖子:

$.ajax({     
    type: 'POST',
    url: "<%= someregistration_path %>",
    data: { "firstname": "text_data_1", "last_name": "text_data2", "authenticity_token": "<%= form_authenticity_token %>" },                                                                                  
    error: function( xhr ){ 
      alert("ERROR ON SUBMIT");
    },
    success: function( data ){ 
      //data response can contain what we want here...
      console.log("SUCCESS, data="+data);
    }
});

【讨论】:

  • 将隐藏的输入字段添加到我的输入表单中解决了我的问题。
  • 如果你使用 Rails 的表单助手,这会自动完成。
【解决方案5】:

从较旧的应用程序升级到 rails 3.1,包括 csrf 元标记仍然无法解决问题。在 ruby​​onrails.org 博客上,他们提供了一些升级技​​巧,特别是应该放在布局头部的这行 jquery:

$(document).ajaxSend(function(e, xhr, options) {
 var token = $("meta[name='csrf-token']").attr("content");
  xhr.setRequestHeader("X-CSRF-Token", token);
});

取自这篇博文:http://weblog.rubyonrails.org/2011/2/8/csrf-protection-bypass-in-ruby-on-rails

在我的例子中,每次 ajax 请求都会重置会话。添加上面的代码解决了这个问题。

【讨论】:

    【解决方案6】:
    1. 确保您的布局中有&lt;%= csrf_meta_tag %&gt;
    2. 添加 beforeSend 以在 ajax 请求中包含 csrf-token 以设置标头。这仅对 post 请求是必需的。

    rails/jquery-ujs 中提供了读取 csrf-token 的代码,因此恕我直言,使用它是最简单的,如下所示:

    $.ajax({
      url: url,
      method: 'post',
      beforeSend: $.rails.CSRFProtection,
      data: {
        // ...
      }
    })
    

    【讨论】:

    • 简单,无需重新实现 Rails 已经包含的内容。这应该是选定的答案。
    • 在 Rails 5.1 中也可以使用:headers: { 'X-CSRF-Token': Rails.csrfToken() }
    • @nathanvda,也许你可以回答这个类似的问题:stackoverflow.com/questions/50159847/…
    【解决方案7】:

    此处投票最多的答案是正确的,但如果您正在执行 跨域 请求,则该答案将不起作用,因为除非您明确告诉 jQuery 传递会话 cookie,否则会话将不可用。这样做的方法如下:

    $.ajax({ 
      url: url,
      type: 'POST',
      beforeSend: function(xhr) {
        xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'))
      },
      xhrFields: {
        withCredentials: true
      }
    });
    

    【讨论】:

    【解决方案8】:

    我只是想把这个链接在这里,因为这篇文章有你正在寻找的大部分答案,而且它也很有趣

    http://www.kalzumeus.com/2011/11/17/i-saw-an-extremely-subtle-bug-today-and-i-just-have-to-tell-someone/

    【讨论】:

      【解决方案9】:

      你可以像下面这样全局编写它。

      普通JS:

      $(function(){
      
          $('#loader').hide()
          $(document).ajaxStart(function() {
              $('#loader').show();
          })
          $(document).ajaxError(function() {
              alert("Something went wrong...")
              $('#loader').hide();
          })
          $(document).ajaxStop(function() {
              $('#loader').hide();
          });
          $.ajaxSetup({
              beforeSend: function(xhr) {xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'))}
          });
      });
      

      咖啡脚本:

        $('#loader').hide()
        $(document).ajaxStart ->
          $('#loader').show()
      
        $(document).ajaxError ->
          alert("Something went wrong...")
          $('#loader').hide()
      
        $(document).ajaxStop ->
          $('#loader').hide()
      
        $.ajaxSetup {
          beforeSend: (xhr) ->
            xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'))
        }
      

      【讨论】:

        【解决方案10】:

        如果您没有使用 jQuery 并且使用类似 fetch API 的请求,您可以使用以下方法获取 csrf-token

        document.querySelector('meta[name="csrf-token"]').getAttribute('content')

        fetch('/users', {
          method: 'POST',
          headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
            'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').getAttribute('content')},
            credentials: 'same-origin',
            body: JSON.stringify( { id: 1, name: 'some user' } )
            })
            .then(function(data) {
              console.log('request succeeded with JSON response', data)
            }).catch(function(error) {
              console.log('request failed', error)
            })
        

        【讨论】:

        【解决方案11】:

        哎呀..

        我在 application.js 中错过了以下行

        //= require jquery_ujs
        

        我更换了它并且它的工作..

        ======= 更新 =========

        5 年后,我又回到了同样的错误,现在我有了全新的 Rails 5.1.6,我又找到了这篇文章。就像生活圈一样。

        现在的问题是: Rails 5.1 默认移除了对 jqueryjquery_ujs 的支持,并添加了

        //= require rails-ujs in application.js
        

        它做了以下事情:

        1. 对各种操作强制确认对话框;
        2. 从超链接发出非 GET 请求;
        3. 使用 Ajax 使表单或超链接异步提交数据;
        4. 在提交表单时自动禁用提交按钮,以防止双击。 (来自:https://github.com/rails/rails-ujs/tree/master

        但为什么它不包括 ajax 请求的 csrf 令牌?如果有人详细了解这一点,请评论我。我很感激。

        无论如何,我在我的自定义 js 文件中添加了以下内容以使其工作(感谢其他答案帮助我获得此代码):

        $( document ).ready(function() {
          $.ajaxSetup({
            headers: {
              'X-CSRF-Token': Rails.csrfToken()
            }
          });
          ----
          ----
        });
        

        【讨论】:

        • 这也是我需要做的。出现这种情况的原因是因为我正在构建一个 React 应用程序来慢慢替换现有的 Rails 应用程序。由于现有应用程序中存在大量 javascript 噪音,因此我创建了一个不同的布局来访问不同的 javascript 文件,但我未能包含 jquery_ujs。这就是诀窍。
        • 是的,有时如果我们在返工时错过了这一点,请重构一些东西..很难找到哪里出了问题。因为我们没有手动做任何事情来使它工作,Rails 自动包含它。我们认为它已经存在。感谢这样的社交 Qn / Ans 网站
        • 也许你可以回答这个类似的问题:stackoverflow.com/questions/50159847/…
        【解决方案12】:

        使用 jquery.csrf (https://github.com/swordray/jquery.csrf)。

        • Rails 5.1 或更高版本

          $ yarn add jquery.csrf
          
          //= require jquery.csrf
          
        • Rails 5.0 或之前的版本

          source 'https://rails-assets.org' do
            gem 'rails-assets-jquery.csrf'
          end
          
          //= require jquery.csrf
          
        • 源代码

          (function($) {
            $(document).ajaxSend(function(e, xhr, options) {
              var token = $('meta[name="csrf-token"]').attr('content');
              if (token) xhr.setRequestHeader('X-CSRF-Token', token);
            });
          })(jQuery);
          

        【讨论】:

        • 在 5.1 中使用 webpack,//= require jquery.csrf 不会工作,对吧?相反,我使用了带有import 'jquery.csrf' 的pack js 文件。请注意,您确实不需要在您的视图中包含此包标签。
        【解决方案13】:

        如果您使用带有 jQ​​uery 的 javascript 在表单中生成令牌,则此方法有效:

        <input name="authenticity_token" 
               type="hidden" 
               value="<%= $('meta[name=csrf-token]').attr('content') %>" />
        

        显然,您需要在 Ruby 布局中包含 &lt;%= csrf_meta_tag %&gt;

        【讨论】:

          【解决方案14】:

          我为这个问题苦苦挣扎了好几天。任何 GET 调用都正常工作,但所有 PUT 都会生成“无法验证 CSRF 令牌真实性”错误。在我向 nginx 添加 SSL 证书之前,我的网站运行良好。

          我终于在我的 nginx 设置中偶然发现了这条缺失的行:

          location @puma { 
              proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 
              proxy_set_header Host $http_host; 
              proxy_redirect off;
              proxy_set_header X-Forwarded-Proto https;   # Needed to avoid 'WARNING: Can't verify CSRF token authenticity'
              proxy_pass http://puma; 
          }
          

          添加缺少的行“proxy_set_header X-Forwarded-Proto https;”后,我所有的 CSRF 令牌错误都退出了。

          希望这可以帮助那些也在用头撞墙的人。哈哈

          【讨论】:

            【解决方案15】:

            对于那些确实需要非 jQuery 答案的人,您可以简单地添加以下内容:

            xmlhttp.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'));

            这里可以举个很简单的例子:

            xmlhttp.open("POST","example.html",true); xmlhttp.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content')); xmlhttp.send();

            【讨论】:

            • 这个选择器不使用jQuery吗?
            • xmlhttp.setRequestHeader('X-CSRF-Token', document.querySelector('meta[name="csrf-token"]').content);
            【解决方案16】:

            如果有人需要与 Uploadify 和 Rails 3.2 相关的帮助(就像我在谷歌上搜索这篇文章时一样),这个示例应用程序可能会有所帮助: https://github.com/n0ne/Uploadify-Carrierwave-Rails-3.2.3/blob/master/app/views/pictures/index.html.erb

            还要检查此应用中的控制器解决方案

            【讨论】:

              【解决方案17】:

              我使用的是 Rails 4.2.4,但不知道为什么会这样:

              Can't verify CSRF token authenticity
              

              我在布局中:

              <%= csrf_meta_tags %>
              

              在控制器中:

              protect_from_forgery with: :exception
              

              调用tcpdump -A -s 999 -i lo port 3000 显示正在设置标题(尽管不需要使用ajaxSetup 设置标题 - 它已经完成):

              X-CSRF-Token: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
              Content-Type: application/x-www-form-urlencoded; charset=UTF-8
              X-Requested-With: XMLHttpRequest
              DNT: 1
              Content-Length: 125
              authenticity_token=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
              

              最后它失败了,因为我关闭了 cookie。如果不启用 cookie,CSRF 将无法工作,因此如果您看到此错误,则可能是另一个原因。

              【讨论】:

              • 非常有帮助!
              猜你喜欢
              • 2016-04-15
              • 2017-07-19
              • 2013-05-30
              • 2011-11-15
              • 2013-06-16
              • 2012-04-27
              • 1970-01-01
              相关资源
              最近更新 更多