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
进行身份验证,但在执行以下操作时发现了一个问题:
引发异常:
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
中修复。
already_signed_out
在config/locales/en.yml
【讨论】:
【参考方案6】:如果您仍然像我在Rails 5
和devise 4.4.1
中那样遇到此问题,请在 app/controllers/application_controller.rb 中更改
protect_from_forgery with: :exception
到
protect_from_forgery with: :null_session
希望对你有帮助。
【讨论】:
【参考方案7】:将其粘贴到布局中:
【讨论】:
以上是关于Devise::SessionsController#destroy 中的 InvalidAuthenticityToken(已注销后注销)的主要内容,如果未能解决你的问题,请参考以下文章