【问题标题】:"ActionController::InvalidAuthenticityToken" error when using form_with使用 form_with 时出现“ActionController::InvalidAuthenticityToken”错误
【发布时间】:2020-09-16 00:25:41
【问题描述】:

我有一个如下所示的表单:

  <%= form_with(url: star.starname, method: :post, local: true) do |f| %> 
    <% star.availabilities.each do |avail| %> 

    <%= f.label avail.time_slot %> 
      <%= radio_button_tag(:time_slot, avail.time_slot) %> <br>
    <% end %> 

  <%= f.submit "Create" %>
  <% end %> 

表单提交后立即:

注意事项:

  • 这发生在应用程序(不是 API)中,因此会话很重要,因此必须保持 CSRF 保护。
  • 问题出现在 chrome、incognito 和 safari 中。
  • 我已尝试使用不同的用户登录并清除 cookie(以防它是由 stale token 引起的)

更多错误信息:

Started POST "/talljohn" for ::1 at 2020-09-16 10:06:21 +1000
Processing by StarsController#book as HTML
  Parameters: {"authenticity_token"=>"P++4a+giwUBqZgCLfMwqKpMu0EGitd8zTOi5RWsnxpKlNcjiuU6hd3ebbIC/IOxlL74RJIvrq+yDuA1ZtfcvFw==", "time_slot"=>"2020-09-16 01:00:00 UTC", "commit"=>"Create", "starname"=>"talljohn"}
Can't verify CSRF token authenticity.
Completed 422 Unprocessable Entity in 1ms (ActiveRecord: 0.0ms | Allocations: 655)


  
ActionController::InvalidAuthenticityToken (ActionController::InvalidAuthenticityToken):
  
actionpack (6.0.3.2) lib/action_controller/metal/request_forgery_protection.rb:215:in `handle_unverified_request'
actionpack (6.0.3.2) lib/action_controller/metal/request_forgery_protection.rb:247:in `handle_unverified_request'
devise (4.7.2) lib/devise/controllers/helpers.rb:255:in `handle_unverified_request'
actionpack (6.0.3.2) lib/action_controller/metal/request_forgery_protection.rb:242:in `verify_authenticity_token'
activesupport (6.0.3.2) lib/active_support/callbacks.rb:428:in `block in make_lambda'
activesupport (6.0.3.2) lib/active_support/callbacks.rb:200:in `block (2 levels) in halting'
actionpack (6.0.3.2) lib/abstract_controller/callbacks.rb:34:in `block (2 levels) in <module:Callbacks>'
activesupport (6.0.3.2) lib/active_support/callbacks.rb:201:in `block in halting'

更新

我恢复到表单的最后一个工作版本,它与上面完全相同,但没有, local: true。然后它突然起作用了! (没有错误)。

我以为local: true(或remote: false)只是关闭了ajax 表单提交。所以我不明白为什么这会产生任何影响(或与 CSRF 有任何关系),这两个方面似乎是不相关的,不清楚为什么这两个概念会相互影响

更新 2

我后来意识到另一个未触及的以前工作的表单也产生了这个错误。它没有以任何方式改变。我在chrome incognito中尝试过,它产生了错误。半小时后(不更改任何代码)我在同一个浏览器中再次尝试它并且它工作。这种(非常)奇怪的行为让我觉得这与会话、cookie 或缓存有关。如果我有任何进一步的了解,我会回来报告

更新 3

在阅读Sarah's solution 后将protect_from_forgery prepend: true 添加到应用程序控制器(我在before_action :authenticate_user! 之前和之后都尝试过),日志中出现相同的错误消息,POST 请求未执行,但应用程序重定向到主页。 IE。在发布后我看到:

Can't verify CSRF token authenticity.
Completed 401 Unauthorized in 1ms (ActiveRecord: 0.0ms | Allocations: 444)


Started GET "/users/sign_in" for ::1 at 2020-09-17 21:08:42 +1000
Processing by Devise::SessionsController#new as HTML
  User Load (0.5ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2  [["id", 1], ["LIMIT", 1]]
Redirected to http://localhost:3000/
Filter chain halted as :require_no_authentication rendered or redirected
Completed 302 Found in 3ms (ActiveRecord: 0.5ms | Allocations: 1900)

更新 4

我尝试手动清除 Rails 片段缓存(使用 Rails.cache.clear )。但是清除片段缓存之前/之后的结果是完全一样的。

【问题讨论】:

  • 您是否尝试过添加&lt;%= hidden_field_tag :authenticity_token, form_authenticity_token %&gt;?您不必这样做,但它可能会在故障排除方面提供一些价值
  • 会不会是缓存相关的?我曾经遇到过类似的问题,这是由于在布局中使用片段缓存 csrf 元标记引起的。
  • @NMPennypacker 我没有那个隐藏字段(除了问题表格中的内容)。但是私有方法看起来像这样:params.permit(:authenticity_token, :time_slot, :starname, :commit)
  • @max 我怀疑缓存可能是原因。所以我尝试了隐身,以不同的用户身份登录,并清除 cookie。还有什么要尝试的吗?
  • 片段缓存独立于客户端,同样适用于任何反向代理缓存。它基本上是服务器为每个客户端提供相同的陈旧内容。

标签: ruby-on-rails ruby-on-rails-6


【解决方案1】:

我记得遇到过这样的事情,并在对ApplicationController 进行任何用户身份验证之前添加protect_from_forgery prepend: true 来解决它。

【讨论】:

  • 非常感谢!我偶然发现了很多 github 线程,尽管我不明白它在做什么。你知道它是否会以任何方式降低安全性?
  • 根据docs,我不这么认为。 If you need to add verification to the beginning of the callback chain, use prepend: true.
【解决方案2】:

TL;DR:经过 2 周的调试尝试后,我关闭了 turbolink,问题就消失了

除了关闭 turbolinks,另一个解决方案似乎是(提到 here)将其添加到 application.js

$(document).on('turbolinks:load', function(){ $.rails.refreshCSRFTokens(); });

上一个答案

问题不断出现。我尝试了以下六件事,但仍然没有解决它

1.清除片段缓存

Rails.cache.clear警告,因为它会清除缓存,它还会删除诸如 sidekiq 作业等内容)。这将删除陈旧的令牌并在浏览器中刷新应用程序将恢复正常,并且表单应该提交(简单的“重新提交”不起作用,所以回到表单页面,刷新,然后提交,它应该工作)

2。硬刷新页面

cmd + opt + j 调出开发者控制台,然后右键单击刷新并选择“Empty Cache and Hard Reload” '

3.删除网站 cookie

Right click on the tiny icon to the immediate left of the url(如果使用 https,则为锁,如果使用 http,则为字母 'i')。进入每个列出的类别(例如“Cookie”、“网站设置”等)并将它们全部删除

4.删除指向同一站点的其他 url 的 cookie

例如,如果您的网站是 www.example.com,并且它托管在 heroku 上的 www.example.herokuapp.com,那么也要删除第二个网址的 cookie

5.删除 localhost 的 cookie

为了确定,我删除了 localhost cookie

6.在completely isolated instances of chrome中测试

【讨论】:

【解决方案3】:

对我来说,解决方案是切换到更具体的 Cookie 策略:

# config/initializers/new_framework_defaults_6_1.rb
# Before: i've used None, but this leads to broken Cookies somehow. Strict or Lax seems to work.
Rails.application.config.action_dispatch.cookies_same_site_protection = :strict

【讨论】:

  • 这是在 Rails 6.1 应用程序中吗?这不是默认的吗?
  • @rainkinz 如您所见,它是通过rails app:update 升级 Rails 时生成的初始化程序,因此在此示例中,当您升级 Rails 应用程序时,它不是默认值。初始化器上方的文档说明,可以使用 :none,但在我们的例子中,只有 :lax 或 :strict 有效,:none 破坏了 cookie。但是,如果您生成一个新的 Rails 应用程序,默认值是 lax
猜你喜欢
  • 2015-11-23
  • 2016-11-14
  • 2018-11-02
  • 1970-01-01
  • 2016-05-12
  • 2011-03-22
  • 2018-06-25
  • 1970-01-01
  • 2015-07-20
相关资源
最近更新 更多