Devise 的 current_user nil 在 ApplicationController 中但不在不同的控制器中(使用简单令牌身份验证)

Devise's current_user nil in ApplicationController but not in a different controller (using Simple Token Authentication)

我有一个 Rails 3.2.22 应用程序 运行 在生产中使用了 +1 年,它使用 Devise 对用户进行身份验证。

我正在尝试实施令牌身份验证,因此我可以使用 Gem 名为简单令牌身份验证 https://github.com/gonzalo-bulnes/simple_token_authentication

按照所有说明进行操作后,我将控制器中的 before_filter :authenticate_user! 替换为 acts_as_token_authentication_handler_for User

gem 与 Devise 集成并默认 回退到 Devise,因此不再需要在控制器中调用 devise;如果参数中缺少令牌(或错误),Devise 将接管。

在我的测试中,如果我将此行添加到 ApplicationController,一切正常,我可以使用 gem 生成的 authentication_token= 秘密登录用户。

但是我不需要 auth 用于 ApplicationController,我需要它用于其他控制器(比如 DashboardController),url 是 /dashboard

如果我将 acts_as_token_authentication_handler_for User 放入该控制器(替换 Devise 的调用),我会遇到最奇怪的情况。

使用 binding.pry,我可以确认 current_user 在模板加载期间设置正确。

但是模板中有一点使用了@last_emails,这是在 ApplicationController.

中的一个方法中定义的

使用 binding.pry,我可以确认 current_usernil

这是代码:

class DashboardController < ApplicationController
  layout 'material'

  acts_as_token_authentication_handler_for User

并且在 ApplicationController 中:

class ApplicationController < ActionController::Base
 layout 'omega'

 before_filter :populate_last_contacts_for_menu 

private
  def populate_last_contacts_for_menu
    if current_user
      @last_contacts = Contact.where("user_id" => current_user.id).where('blocked != ? or blocked is null', true).last(10).reverse
    end
  end

有趣的是:使用 binding.pry,就像我说的,我可以检查模板中是否定义了 current_user(这意味着 sign_in 是成功的)。它甚至在更好的错误控制台中定义。但是,如果我转到主页,我会看到该用户未登录...

为此我在网上找遍了:阅读 Gem 的 github 中的所有问题以及 SO 中关于 current_user 为零的所有帖子,但没有光一点。

我的 devise_for :users 不在 routes.rb 的任何范围内,正如我所说,我在整个应用程序中多次调用 current_user,这是我第一次调用Devise 的问题。

当您在 DashboardController 中调用 acts_as_token_authentication_handler_for 指令时,它会声明一些 before_filters 以便控制器对用户进行身份验证。

但问题是当你继承rails控制器时,首先执行父控制器的过滤器,然后执行子控制器的过滤器。

父控制器是ApplicationController。在它的 populate_last_contacts_for_menu 过滤器被调用的那一刻,用户没有被认证,因为 acts_as_token_authentication_handler_for 指令给出的认证过滤器还没有被调用,它们是在子控制器中声明的。

可能的解决方案:

1) 尝试附加 populate_last_contacts_for_menu 过滤器:

append_before_filter :populate_last_contacts_for_menu

我不确定它是否适用于你的情况,但你可以尝试找出答案。

2) 在 ApplicationControoler 中调用 acts_as_token_authentication_handler_for 指令,并以某种方式为不需要它的控制器跳过它。 (我不喜欢这种方式,但如果第一种方式不起作用,它可能会有所帮助。)

3) 将 populate_last_contacts_for_menu 过滤器逻辑移动到助手中。我认为这是最好的解决方案。此逻辑不属于控制器。当请求不是 'get' 时,此过滤器不执行任何操作,因为在这种情况下您不需要呈现视图。

module ApplicationHelper

  def last_contacts
    @last_contacts ||= if signed_in?
      Contact.where("user_id" => current_user.id).where('blocked != ? or blocked is null', true).last(10).reverse
    else
      []
    end
  end
  ...
end

# View:

<% if last_contacts.present? %>
  ....
<% end %>