【问题标题】:Devise's current_user nil in ApplicationController but not in a different controller (using Simple Token Authentication)在 ApplicationController 中设计 current_user nil 但不在其他控制器中(使用简单令牌身份验证)
【发布时间】:2016-02-15 14:55:32
【问题描述】:

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

我正在尝试实现令牌身份验证,因此我可以使用名为 Simple Token Authentication https://github.com/gonzalo-bulnes/simple_token_authentication 的 Gem 发送带有 URL 参数的交易电子邮件,这些参数可以自动登录用户。

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

gem 与 Devise 集成并具有 default 后备功能,因此不再需要在控制器中调用 devise;如果参数中缺少令牌(或错误),Devise 将接管。

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

但我不需要 auth 用于 ApplicationController,我需要它用于其他控制器(如 DashboardController),网址为 /dashboard

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

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

但是模板中有一点使用@last_emails,它在ApplicationController 的方法中定义。

使用 binding.pry,我可以确认 current_user 在那里 nil

这是代码:

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 为 nil 的所有帖子,但一点也不亮。

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

【问题讨论】:

    标签: ruby-on-rails ruby-on-rails-3 authentication devise


    【解决方案1】:

    当您在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 %>
    

    【讨论】:

    • 即使很难,我也理解你的解释,你能解释一下为什么 Devise 没有发生同样的问题吗,因为我刚刚将 before_filter: authenticate_user! 替换为新的身份验证方法,在同一个控制器中也继承自应用控制器?此外,您将populate_last_contacts_for_menu 移动到助手的解决方案有效,但我避免为此更改任何应用程序的逻辑。原因很简单:如果我只是将身份验证方法从 Devise 更改为集成和扩展 Devise 的 Gem,为什么要重写任何一行?
    • 该设计的current_user 方法不需要在每个请求都执行auhenticate_user! 过滤器。一旦调用了authenticate_user! 方法,它就会在会话中设置当前用户ID,并且在下一个请求中,即使authenticate_user! 方法没有被调用,'current_user' 方法也会返回当前用户。似乎 Simple Token Authentication gem 具有不同的行为。在设置 current_user 和进行身份验证需要过滤器之前,而设计的 authenticate_user! 过滤器和 current_user 方法独立工作。
    猜你喜欢
    • 1970-01-01
    • 2014-11-30
    • 1970-01-01
    • 2016-03-22
    • 1970-01-01
    • 1970-01-01
    • 2011-11-06
    • 2020-08-28
    • 2015-02-11
    相关资源
    最近更新 更多