Devise::SessionsController#destroy 中的 InvalidAuthenticityToken(已注销后注销)

Posted

技术标签:

【中文标题】Devise::SessionsController#destroy 中的 InvalidAuthenticityToken(已注销后注销)【英文标题】:InvalidAuthenticityToken in Devise::SessionsController#destroy (sign out after already having signed out) 【发布时间】:2014-04-24 14:26:27 【问题描述】:

我正在使用 Devise 3.2.0 进行身份验证,但在执行以下操作时发现了一个问题:

标签 1:登录到应用 标签 2:转到应用中的任何页面 标签 2:退出退出(成功) 标签 1:退出 (失败 - 请参阅下面的例外情况)

引发异常:

ActionController::InvalidAuthenticityToken in Devise::SessionsController#destroy

在开发日志中我看到:

无法验证 CSRF 令牌的真实性

堆栈跟踪的前三行是:

ActionController::InvalidAuthenticityToken (ActionController::InvalidAuthenticityToken):
  actionpack (4.0.0) lib/action_controller/metal/request_forgery_protection.rb:163:in `handle_unverified_request'
  actionpack (4.0.0) lib/action_controller/metal/request_forgery_protection.rb:170:in `handle_unverified_request'
  devise (3.2.0) lib/devise/controllers/helpers.rb:198:in `handle_unverified_request'

如何确保连续退出不会引发异常?

【问题讨论】:

它不是连续退出,当您已经退​​出时,您正在尝试退出。那么,当您从 tab2 注销时,为什么不从 tab1 注销用户 我不知道你为什么说这不是连续退出。 连续意味着一个接一个。如果我在退出另一个选项卡后尝试在一个选项卡中退出,就我而言,这是连续的。但关键是,这是用户可以采取的非常合理的操作。举一个例子,用户在一个站点上打开多个标签页,当他们在一个标签页中退出,然后继续关闭其他标签页时,他们可能会尝试再次退出(由于登录的外观所有其他选项卡)。 @user664833 现在我明白了。是的,Devise 应该已经处理了注销场景而不会引发讨厌的错误。但同样,他们可能会说这就是我们所说的customization。在 Devise 实施之前,KirtiThorat 的解决方法可能是更好的解决方案。 在 Devise Github Repo 上查看 @freddyrangel github.com/plataformatec/devise/issues/2934 提交的问题。您可以通过支持该问题来参与讨论。 【参考方案1】:

这是发生了什么,

当您最初从选项卡 2 注销时,与已登录用户关联的 session 和authentity_token 已被销毁。 当您尝试从选项卡 1 注销时,Devise 再次尝试使用在选项卡 2 上销毁的authentity_token 销毁会话。

因此,您会收到错误 ActionController::InvalidAuthenticityToken,因为设计无法使用给定的 authenticity_token 进行身份验证。

每次登录您只能获得一个独特的会话,如果该会话被破坏,您将没有任何东西可以再次破坏。

编辑

Devise 不提供此行为。如果你想实现这样的行为,你必须重写 SessionsController。

app/controllers/users目录下创建sessions_controller.rb文件

class Users::SessionsController < Devise::SessionsController
  prepend_before_filter :verify_user, only: [:destroy]

  private
  ## This method intercepts SessionsController#destroy action 
  ## If a signed in user tries to sign out, it allows the user to sign out 
  ## If a signed out user tries to sign out again, it redirects them to sign in page
  def verify_user
    ## redirect to appropriate path
    redirect_to new_user_session_path, notice: 'You have already signed out. Please sign in again.' and return unless user_signed_in?
  end
end

更新routes.rb

devise_for :users, :controllers =>  :sessions => "users/sessions" 

【讨论】:

谢谢。我了解为什么会发生这种情况,但我想知道如何在不引发异常的情况下使其工作。例如,如果您在 Twitter 或几乎任何其他网站上执行相同的操作,则所有连续的注销尝试都会成功 - 只要没有引发异常,并使用户相信他们刚刚签名退出(不管他们实际上已经退出)。 @KirtiThorat 我认为如果用户收到一条消息会更好,例如“您已经退出,请重新登录”然后重定向到登录页面而不是root_path。 @user3317140 +1 这听起来是个好主意。将其添加到代码中。 @KirtiThorat 看起来不错。您(或 @freddyrangel)应向 Devise 提交针对此解决方法的拉取请求。【参考方案2】:

Kirti 完全正确。我昨天遇到了这个问题,但是使用了自定义身份验证解决方案。如果这确实是您想要解决的问题,您可以弄清楚如何覆盖 Devise 的注销操作并为该操作添加 skip_before_filter :verify_authenticity_token

【讨论】:

是的,我意识到我可以覆盖 Devise 的注销操作,但我很惊讶 Devise 还没有正确处理这件事,因为这不是不常见的用户行为。令我惊讶的是,Devise 会将这个松散的结局留给开发人员来解决。这种行为在任何方面是如何令人满意的? 你说得很好。实际上,我实际上可能会提交一个拉取请求。 谢谢你 - 请做。 IE。如果用户已经退出,​​则绕过通常的代码,让它看起来好像用户现在退出了。 @freddyrangel 如果您有解决方法,请提交拉取请求。这种默认的不良行为必须消失 我已经在 github 上提交了一个问题,看看之前是否已经解决了。我的感觉是,Devise 窥视者在安全性方面真的很重要,所以他们可能不会继续尝试可能会或可能不会危及安全性的事情。【参考方案3】:

您可以更改验证 csrf 令牌的策略。

在 rails 3 中,验证失败时的默认策略是返回一个空会话。在 rails 4 中更改了 application_controller 中的策略以返回异常。

我解决了这个问题,更改了我的 application_controller.rb

class ApplicationController < ActionController::Base
-    protect_from_forgery, with: :exception
+    protect_from_forgery

这样,使用默认策略。

【讨论】:

【参考方案4】:

解决此问题的一个简单方法是允许通过 GET 而不是 DELETE 退出。在 devise.rb 中,您可以简单地更改为:

# The default HTTP method used to sign out a resource. Default is :delete.
config.sign_out_via = :get

【讨论】:

@CodeWalrus,根据最新的提交,初始化文件似乎没有任何变化。您确定这不是其他问题的结果吗? github.com/plataformatec/devise/blob/…【参考方案5】:

此错误已在 devise 3.3.0 中修复。

见change log for 3.3.0 见file changes in pull request #2968 注意already_signed_outconfig/locales/en.yml

【讨论】:

【参考方案6】:

如果您仍然像我在Rails 5devise 4.4.1 中那样遇到此问题,请在 app/controllers/application_controller.rb 中更改

protect_from_forgery with: :exception

protect_from_forgery with: :null_session

希望对你有帮助。

【讨论】:

【参考方案7】:

将其粘贴到布局中:

【讨论】:

以上是关于Devise::SessionsController#destroy 中的 InvalidAuthenticityToken(已注销后注销)的主要内容,如果未能解决你的问题,请参考以下文章