【问题标题】:ActionController::InvalidAuthenticityToken in Rails 5 with post onlyRails 5 中的 ActionController::InvalidAuthenticityToken 仅发布
【发布时间】:2017-04-12 21:36:36
【问题描述】:

我有一个简单的应用程序,我正在使用 Rails 5.0.0.1(该应用程序被称为 simpletest - 它尽可能简单)以便在 Rails 5 中找出一些东西。我有点奇怪错误。

这里是 config/routes.rb

Rails.application.routes.draw do
  root "foo#bar"
  get "dobaz" => "foo#bar"
  match "dobaz" => "foo#baz", via: :post
end

这里是 app/controllers/foo_controller.rb

class FooController < ApplicationController
  def baz
      if session[:qux].nil? || params[:reset] == "true"
        session[:qux] = 0
      else
        session[:qux] += 1
      end
      @qux = session[:qux]
      @format = request.format
      render 'bar'

  end
end

这里是apps/views/foo/bar.html.erb

<p>Qux: <%= @qux %></p>
<p>Format: <%= @format %></p>
<form action="/dobaz" method="post">
<p>Reset:
<input type="radio" name="reset" value="true">True</input>
<input type="radio" name="reset" value="false">False</input>
<input type="submit"/>
</form>

我没有使用任何身份验证或登录或其他任何东西。我(显然)正在使用会话。

当我尝试实际执行应用程序时,我在异常页面上收到以下错误:

ActionController::InvalidAuthenticityToken in FooController#baz

我已经检查了一堆答案。我在 apps/views/layouts/application.html.erb 中有 &lt;%= csrf_meta_tags %&gt;。

这只发生在帖子中。使用get,它工作正常。但是,我需要使用 post。

此外,还有一个解决方案,但对我来说没有意义。我可以将以下代码添加到我的控制器的顶部,如果格式是 json,它(如果我理解正确的话)应该重置会话,并且突然它起作用了:

protect_from_forgery with: :reset_session, if: ->{request.format.json?}

但格式是text/html!此外,会话没有被重置(正如我可以通过session[:qux] 知道的那样。为什么这会有任何影响?如果我只是使用:

protect_from_forgery with: :reset_session

它可以工作,但它当然会重置会话。使用 :null_session 代替 :reset_session 具有相同的效果(无论是否使用 if)。 我没有做任何特别的事情。除了添加代码,我所做的只是:

rails new simpletest
[copied over Gemfile]
bundle install
rails generate controller foo bar

我在 Debian 8 上运行 ruby​​ 2.3.0p0(2015-12-25 修订版 53290)[x86_64-linux]。

【问题讨论】:

  • 提交表单时,看到authentity_token参数了吗? (你可以在底部的错误页面上看到请求中的参数列表)
  • &lt;input type='hidden' name='authenticity_token' value='&lt;%= form_authenticity_token %&gt;'&gt;

标签: ruby-on-rails ruby session ruby-on-rails-5 csrf-protection


【解决方案1】:

问题是您需要使用表单助手,它会自动为authentity_token 设置隐藏输入。

在您看来,您可以只使用 form_tag (这是这种类型的最简单的助手)

<!-- in app/views/foo/bar.html.erb -->
<%= form_tag "/dobaz" do %>
  <p>Reset:
  <input type="radio" name="reset" value="true">True</input>
  <input type="radio" name="reset" value="false">False</input>
  <input type="submit"/>
<% end %>

【讨论】:

  • 显然我没有资格获得重要的投票,但对于未来的探索者来说,这个解决方案非常有效,并且与@cdimitroulas 的回答一起,解释了为什么protect_from_forgery with: :reset_session, if: -&gt;{request.format.json?} 有效,即使它不适用.
【解决方案2】:

正如 jphager2 提到的,这是 Rails 在使用输入表单时针对 CSRF(跨站点请求伪造)的保护导致的问题,可以通过使用表单助手而不是简单的 HTML 表单轻松解决。

您可以在此处查看 Faisal 对此主题的精彩回答 -> Understanding the Rails Authenticity Token

【讨论】:

  • 谢谢,这真的很有帮助。关于这个有很多线程,我没有找到这个(我可能过于关注错误消息作为搜索文本)。如果我理解正确,protext_from_forgery with: 起作用的原因是它强制使用身份验证令牌。这看起来对吗?
  • 我对 Rails 还很陌生,但据我所知,这是正确的!
猜你喜欢
  • 2018-11-02
  • 2017-11-13
  • 2016-11-14
  • 1970-01-01
  • 2021-12-20
  • 2018-11-27
  • 2015-05-21
  • 2016-07-06
  • 2017-09-07
相关资源
最近更新 更多