设计:可记住意味着last_sign_in_at不会被可跟踪更新

时间:2012-01-09 13:06:25

标签: ruby-on-rails authentication devise warden

我一直在使用Devise,并依赖于用户模型的last_sign_in_at来计算我的客户是否在X天内没有返回。但是,我最近发现last_sign_in_at仅在实际的表单登录事件发生时更新,而不是由于包含可记住的用户而自动登录时。

如果想确保每次用户登录时更新last_sign_in_at(新的浏览器会话),无论他们是使用表单登录还是由可记忆的cookie自动登录,我将如何进行操作这与设计兼容吗?

5 个答案:

答案 0 :(得分:15)

采用Matthew的解决方案,我认为代码应该如下(注意会话之前的not-operator [:logged_signin]):

before_filter :update_last_sign_in_at

protected

def update_last_sign_in_at
  if user_signed_in? && !session[:logged_signin]
    sign_in(current_user, :force => true)
    session[:logged_signin] = true
  end
end

答案 1 :(得分:6)

可跟踪的挂钩来自Warden's after_set_user挂钩 - 您可以轻松解决此问题,设置一个before_filter来调用sign_in

这可以进行优化,但要测试是否使用

before_filter proc{ sign_in(current_user, :force => true) }

更新last_signed_in_at时间戳。

答案 2 :(得分:2)

Devise: rememberable means that last_sign_in_at is not updated by trackable

扩展之前的解决方案,问题是如果用户正常登录,他们将“登录两次”。这会将last_sign_in_at设置为与current_sign_in_at相同(或几乎相同)的值。 在我的网站上,我使用last_sign_in_at让用户知道自上次访问网站以来发生了什么,因此我需要它有点准确。此外,它记录+1登录计数。

此外,还有人(像我一样)将浏览器窗口打开几天而不关闭它(因此永远不会清除会话标志)。对于度量目的等,如果此类用户行为有时会刷新current_sign_in_at时间,则会很有用。

以下变体将解决这些问题。

class ApplicationController < ActionController::Base
  before_filter :update_sign_in_at_periodically
  UPDATE_LOGIN_PERIOD = 10.hours

  protected

  def update_sign_in_at_periodically
    if !session[:last_login_update_at] or session[:last_login_update_at] < UPDATE_LOGIN_PERIOD.ago
      session[:last_login_update_at] = Time.now
      sign_in(current_user, :force => true) if user_signed_in?
    end
  end
end

然而,当我尝试使用Devise 3.2.4时,我会在通过cookie自动登录时获得新的登录(登录计数+1和current_sign_in_at被设置)。所以,我只留下想要跟踪定期更新的问题,即使对于保持会话开放的用户也是如此。

class ApplicationController < ActionController::Base
  before_filter :update_sign_in_at_periodically
  UPDATE_LOGIN_PERIOD = 10.hours 

  protected 

  def update_sign_in_at_periodically
    # use session cookie to avoid hammering the database
    if !session[:last_login_update_at] or session[:last_login_update_at] < UPDATE_LOGIN_PERIOD.ago
      session[:last_login_update_at] = Time.now
      if user_signed_in? and current_user.current_sign_in_at < 1.minute.ago # prevents double logins
        sign_in(current_user, :force => true)
      end
    end
  end
end

答案 3 :(得分:0)

application_controller上,您可以设置一个before_action来检查当前用户的current_sign_in_at是否比X前更长。如果是,则使用更新current_sign_in_at的sign_in(current_user, force: true)

before_action :update_last_sign_in_at

def update_last_sign_in_at
   return unless user_signed_in? && current_user.current_sign_in_at < 12.hours.ago
   sign_in(current_user, force: true)
end

我使用它来检测非活动用户(未登录6个月)并删除它们。 #GDPR

答案 4 :(得分:-1)

AFAIK您还可以在update_tracked_fields!型号上使用current_user