24

出于某种原因,在使用 json 或 xml 向我的应用程序发出 post 请求时,我得到了 InvalidAuthenticityToken。我的理解是,rails 应该只对 html 或 js 请求需要一个真实性令牌,因此我不应该遇到这个错误。到目前为止,我发现的唯一解决方案是对我想通过 API 访问的任何操作禁用protect_from_forgery,但由于显而易见的原因,这并不理想。想法?

    def create
    respond_to do |format|
        format.html
        format.json{
            render :json => Object.create(:user => @current_user, :foo => params[:foo], :bar => params[:bar])
        }
        format.xml{
            render :xml => Object.create(:user => @current_user, :foo => params[:foo], :bar => params[:bar])
        }
    end
end

每当我将请求传递给操作时,这就是我在日志中得到的内容:

 Processing FooController#create to json (for 127.0.0.1 at 2009-08-07 11:52:33) [POST]
 Parameters: {"foo"=>"1", "api_key"=>"44a895ca30e95a3206f961fcd56011d364dff78e", "bar"=>"202"}

ActionController::InvalidAuthenticityToken (ActionController::InvalidAuthenticityToken):
  thin (1.2.2) lib/thin/connection.rb:76:in `pre_process'
  thin (1.2.2) lib/thin/connection.rb:74:in `catch'
  thin (1.2.2) lib/thin/connection.rb:74:in `pre_process'
  thin (1.2.2) lib/thin/connection.rb:57:in `process'
  thin (1.2.2) lib/thin/connection.rb:42:in `receive_data'
  eventmachine (0.12.8) lib/eventmachine.rb:242:in `run_machine'
  eventmachine (0.12.8) lib/eventmachine.rb:242:in `run'
  thin (1.2.2) lib/thin/backends/base.rb:57:in `start'
  thin (1.2.2) lib/thin/server.rb:156:in `start'
  thin (1.2.2) lib/thin/controllers/controller.rb:80:in `start'
  thin (1.2.2) lib/thin/runner.rb:174:in `send'
  thin (1.2.2) lib/thin/runner.rb:174:in `run_command'
  thin (1.2.2) lib/thin/runner.rb:140:in `run!'
  thin (1.2.2) bin/thin:6
  /opt/local/bin/thin:19:in `load'
  /opt/local/bin/thin:19
4

7 回答 7

28

启用后,protect_from_forgeryRails 需要一个真实性令牌来处理任何非 GET 请求。Rails 将自动在使用表单助手创建的表单或使用 AJAX 助手创建的链接中包含真实性令牌——所以在正常情况下,您不必考虑它。

如果您没有使用内置的 Rails 表单或 AJAX 帮助程序(也许您正在做不引人注目的 JS 或使用 JS MVC 框架),您必须自己在客户端设置令牌并将其与您的提交 POST 请求时的数据。<head>你会在你的布局中加入这样的一行:

<%= javascript_tag "window._token = '#{form_authenticity_token}'" %>

然后您的 AJAX 函数会将令牌与您的其他数据一起发布(使用 jQuery 的示例):

$.post(url, {
    id: theId,
    authenticity_token: window._token
});
于 2009-10-26T22:59:21.223 回答
12

我遇到了类似的情况,问题是我没有通过正确的内容类型标头发送 - 我正在请求text/json并且我应该一直在请求application/json

我使用curl以下内容来测试我的应用程序(根据需要进行修改):

curl -H "Content-Type: application/json" -d '{"person": {"last_name": "Lambie","first_name": "Matthew"}}' -X POST http://localhost:3000/people.json -i

或者您可以将 JSON 保存到本地文件并curl像这样调用:

curl -H "Content-Type: application/json" -v -d @person.json -X POST http://localhost:3000/people.json -i

当我将内容类型标题更改为右侧application/json时,我所有的麻烦都消失了,我不再需要禁用伪造保护。

于 2009-10-22T16:54:15.567 回答
12

这与@user1756254 的答案相同,但在 Rails 5 中,您需要使用更多不同的语法:

protect_from_forgery unless: -> { request.format.json? }

来源:http ://api.rubyonrails.org/v5.0/classes/ActionController/RequestForgeryProtection.html

于 2016-09-19T21:27:12.740 回答
3

添加到 andymism 的答案中,您可以使用它在每个 POST 请求中应用 TOKEN 的默认包含:

$(document).ajaxSend(function(event, request, settings) {
    if ( settings.type == 'POST' ||  settings.type == 'post') {
        settings.data = (settings.data ? settings.data + "&" : "")
            + "authenticity_token=" + encodeURIComponent( window._token );
    }
});
于 2009-10-28T00:38:50.030 回答
3

从 Rails 4.2 开始,我们有另一种方法是在 Rails 应用程序中使用 skip_before_filter 来避免 verify_authenticity_token :

skip_before_action :verify_authenticity_token, only: [:action1, :action2]

这将让 curl 完成它的工作。

Ruby on Rails 4.2 发行说明:https ://guiarails.com.br/4_2_release_notes.html

于 2014-02-05T21:15:44.657 回答
2

只要 JavaScript 存在于 Rails 服务的网站上(例如:JS 片段;或通过 webpacker 管理的 React 应用程序),您就可以默认使用 include incsrf_meta_tagsapplication.html.erb的值。

application.html.erb

<html>
  <head>
    ...
    <%= csrf_meta_tags %>
    ...

因此,在您网站的 HTML 中:

<html>
  <head>
    <meta name="csrf-token" content="XZY">

从属性中获取令牌content并在请求中使用它:

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

const response = await fetch("/entities/1", {
  method: 'PATCH',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({ authenticity_token: token, entity: { name: "new name" } })
});

这类似于@andrewle 的答案,但不需要额外的令牌。

于 2021-01-26T13:49:11.860 回答
1

要添加到费尔南多的答案,如果您的控制器同时响应 json 和 html,您可以使用:

      skip_before_filter :verify_authenticity_token, if: :json_request?
于 2014-12-19T19:46:43.960 回答