Rails - json/xml 请求的 InvalidAuthenticityToken

发布于 2024-07-30 17:59:55 字数 1767 浏览 3 评论 0原文

由于某种原因,我在使用 json 或 xml 向我的应用程序发出发布请求时收到 InvalidAuthenticityToken。 我的理解是,rails 应该只需要 html 或 js 请求的真实性令牌,因此我不应该遇到这个错误。 到目前为止,我找到的唯一解决方案是对我想通过 API 访问的任何操作禁用 protected_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

For some reason I'm getting an InvalidAuthenticityToken when making post requests to my application when using json or xml. My understanding is that rails should require an authenticity token only for html or js requests, and thus I shouldn't be encountering this error. The only solution I've found thus far is disabling protect_from_forgery for any action I'd like to access through the API, but this isn't ideal for obvious reasons. Thoughts?

    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

and this is what I get in the logs whenever I pass a request to the action:

 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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

我的影子我的梦 2024-08-06 17:59:56

添加到 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 );
    }
});

Adding up to andymism's answer you can use this to apply the default inclusion of the TOKEN in every POST request:

$(document).ajaxSend(function(event, request, settings) {
    if ( settings.type == 'POST' ||  settings.type == 'post') {
        settings.data = (settings.data ? settings.data + "&" : "")
            + "authenticity_token=" + encodeURIComponent( window._token );
    }
});
骑趴 2024-08-06 17:59:56

只要 JavaScript 位于 Rails 提供的网站上(例如:JS 片段;或通过 webpacker 管理的 React 应用程序),您就可以使用 application.html 中包含的 csrf_meta_tags 中的值默认为.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 的答案但不需要额外的令牌。

As long as the JavaScript lives on the website served by Rails (for example: a JS snippet; or React app managed via webpacker) you can use the the value in csrf_meta_tags included in application.html.erb by default.

In application.html.erb:

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

Therefore in the HTML of your website:

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

Grab the token from the content property and use it in the request:

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" } })
});

This is similar to @andrewle's answer but there's no need for an additional token.

雪落纷纷 2024-08-06 17:59:56

从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

Since Rails 4.2, we have another way is to avoid verify_authenticity_token using skip_before_filter in your Rails App:

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

This will let curl to do its job.

Ruby on Rails 4.2 Release Notes: https://guiarails.com.br/4_2_release_notes.html

一萌ing 2024-08-06 17:59:56

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

      skip_before_filter :verify_authenticity_token, if: :json_request?

To add to Fernando's answer, if your controller responds to both json and html, you can use:

      skip_before_filter :verify_authenticity_token, if: :json_request?
魂牵梦绕锁你心扉 2024-08-06 17:59:55

启用 protect_from_forgery 后,Rails 需要为任何非 GET 请求提供真实性令牌。 Rails 会自动在使用表单助手创建的表单或使用 AJAX 助手创建的链接中包含真实性令牌——因此在正常情况下,您不必考虑它。

如果您没有使用内置的 Rails 表单或 AJAX 帮助程序(也许您正在使用不显眼的 JS 或使用 JS MVC 框架),您必须自己在客户端设置令牌并将其与您的提交 POST 请求时的数据。 您可以在布局的 中放置这样一行:

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

然后您的 AJAX 函数将使用您的其他数据发布令牌(例如使用 jQuery):

$.post(url, {
    id: theId,
    authenticity_token: window._token
});

With protect_from_forgery enabled, Rails requires an authenticity token for any non-GET requests. Rails will automatically include the authenticity token in forms created with the form helpers or links created with the AJAX helpers--so in normal cases, you won't have to think about it.

If you're not using the built-in Rails form or AJAX helpers (maybe you're doing unobstrusive JS or using a JS MVC framework), you'll have to set the token yourself on the client side and send it along with your data when submitting a POST request. You'd put a line like this in the <head> of your layout:

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

Then your AJAX function would post the token with your other data (example with jQuery):

$.post(url, {
    id: theId,
    authenticity_token: window._token
});
风苍溪 2024-08-06 17:59:55

我遇到了类似的情况,问题是我没有通过正确的内容类型标头发送 - 我正在请求 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

当我更改内容时在右侧输入 headers application/json 我所有的麻烦都消失了,我不再需要禁用伪造保护。

I had a similar situation and the problem was that I was not sending through the right content type headers - I was requesting text/json and I should have been requesting application/json.

I used curl the following to test my application (modify as necessary):

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

Or you can save the JSON to a local file and call curl like this:

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

When I changed the content type headers to the right application/json all my troubles went away and I no longer needed to disable forgery protection.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文