这是我现在的代码:
class SessionTimeoutController < ApplicationController before_filter :authenticate_user! # Calculates the number of seconds until the user is # automatically logged out due to inactivity. Unlike most # requests,it should not reset the timeout countdown itself. def check_time_until_logout @time_left = current_user.timeout_in end # Determines whether the user has been logged out due to # inactivity or not. Unlike most requests,it should not reset the # timeout countdown itself. def has_user_timed_out @has_timed_out = current_user.timedout? (Time.now) end # Resets the clock used to determine whether to log the user out # due to inactivity. def reset_user_clock # Receiving an arbitrary request from a client automatically # resets the Devise Timeoutable timer. head :ok end end
SessionTimeoutController#reset_user_clock的作用是因为每次RoR收到来自经过身份验证的用户的请求时,Timeoutable#timeout_in将重置为我在Devise#timeout_in中配置的任何内容.如何在check_time_until_logout和has_user_timed_out中阻止重置?
解决方法
class SessionTimeoutController < ApplicationController # These are what prevent check_time_until_logout and # reset_user_clock from resetting users' Timeoutable # Devise "timers" prepend_before_action :skip_timeout,only: [:check_time_until_logout,:has_user_timed_out] def skip_timeout request.env["devise.skip_trackable"] = true end skip_before_filter :authenticate_user!,only: [:has_user_timed_out] def check_time_until_logout @time_left = Devise.timeout_in - (Time.now - user_session["last_request_at"]).round end def has_user_timed_out @has_timed_out = (!current_user) or (current_user.timedout? (user_session["last_request_at"])) end def reset_user_clock # Receiving an arbitrary request from a client automatically # resets the Devise Timeoutable timer. head :ok end end
这些是我所做的改变:
使用env [“devise.skip_trackable”]
这是阻止Devise重新设置由于不活动而在登录用户之前等待多长时间的代码:
prepend_before_action :skip_timeout,:has_user_timed_out] def skip_timeout request.env["devise.skip_trackable"] = true end
该代码更改了Devise在内部使用的哈希值,以决定是否更新其存储的值以跟踪用户上次的活动时间.具体来说,这是我们正在与(link)交互的Devise代码:
Warden::Manager.after_set_user do |record,warden,options| scope = options[:scope] env = warden.request.env if record && record.respond_to?(:timedout?) && warden.authenticated?(scope) && options[:store] != false last_request_at = warden.session(scope)['last_request_at'] if record.timedout?(last_request_at) && !env['devise.skip_timeout'] warden.logout(scope) if record.respond_to?(:expire_auth_token_on_timeout) && record.expire_auth_token_on_timeout record.reset_authentication_token! end throw :warden,:scope => scope,:message => :timeout end unless env['devise.skip_trackable'] warden.session(scope)['last_request_at'] = Time.now.utc end end end
(请注意,每次Rails处理来自客户端的请求时,都会执行此代码.)
接近尾声的这些线对我们很有兴趣:
unless env['devise.skip_trackable'] warden.session(scope)['last_request_at'] = Time.now.utc end
这是“重置”倒计时的代码,直到用户由于不活动而退出.只有在env [‘devise.skip_trackable’]不正确的情况下才执行,所以我们需要在Devise处理用户请求之前更改该值.
为了做到这一点,我们告诉Rails在做任何事情之前更改env [‘devise.skip_trackable’]的值.再次,从我的最终代码:
prepend_before_action :skip_timeout,:has_user_timed_out] def skip_timeout request.env["devise.skip_trackable"] = true end
这一点以上的一切都是我需要改变来回答我的问题.还有一些其他的变化,我需要让我的代码工作,因为我想要的,所以我会在这里记录他们.
正确使用超时
我错读了关于Timeoutable模块的文档,所以我的问题中的代码有一些其他问题.
首先,我的check_time_until_logout方法总是返回相同的值.这是我的动作的错误版本:
def check_time_until_logout @time_left = current_user.timeout_in end
我认为Timeoutable#timeout_in将返回用户自动注销的时间.相反,它返回在记录用户之前配置Devise所需等待的时间.我们需要计算用户离开我们自己的时间.
为了计算这个,我们需要知道用户最后一次有Devise识别的活动.来自我们上面看到的Devise源代码的这段代码决定了用户最后一次活动的时间:
last_request_at = warden.session(scope)['last_request_at']
我们需要获取由warden.session(scope)返回的对象的句柄.它看起来像user_session哈希,Devise为我们提供了像handle_handler和user_signed_in?这样的对象.
使用user_session哈希并计算剩余时间,check_time_until_logout方法将变为
def check_time_until_logout @time_left = Devise.timeout_in - (Time.now - user_session["last_request_at"]).round end
我也误读了Timeoutable#timedout的文档.它会检查用户在用户上次激活时通过的时间是否超时,而不是当前通过的时间.我们需要做的更改是直接的:而不是传入Time.now,我们需要在user_session哈希中传递时间:
def has_user_timed_out @has_timed_out = (!current_user) or (current_user.timedout? (user_session["last_request_at"])) end
一旦我做了这三个变化,我的控制器按照我的预期方式行事.