设计 - 发出请求而不重置倒计时,直到用户因不活动而注销 [英] Devise - Make a request without resetting the countdown until a user is logged out due to inactivity

查看:18
本文介绍了设计 - 发出请求而不重置倒计时,直到用户因不活动而注销的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用 Devise 开发一个 RoR 应用程序.我想让客户端向服务器发送请求,以查看在客户端上的用户由于不活动而自动注销之前还剩多少时间(使用 Timeoutable 模块).我不希望此请求导致 Devise 在用户注销之前重置倒计时.我该如何配置?

I'm working on a RoR app with Devise. I want to let clients send a request to the server to see how much time is left until the user on the client is automatically logged out due to inactivity (using the Timeoutable module). I don't want this request to cause Devise to reset the countdown until the user is logged off. How can I configure this?

这是我现在的代码:

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_logouthas_user_timed_out 中的重置?

SessionTimeoutController#reset_user_clock works because every time RoR receives a request from an authenticated user, Timeoutable#timeout_in is reset to whatever I have configured in Devise#timeout_in. How do I prevent that reset in check_time_until_logout and has_user_timed_out?

推荐答案

我最终不得不对我的代码进行几处更改.当我完成时,我会展示我最终得到的结果,然后解释它的作用:

I ended up having to make several changes to my code. I'll show what I ended up with when I was finished, then explain what it does:

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 重置用户因不活动而退出之前等待多长时间的代码:

This is the code that prevents Devise from resetting how long it waits before logging a user out due to inactivity:

  prepend_before_action :skip_timeout, only: [:check_time_until_logout, :has_user_timed_out]
  def skip_timeout
    request.env["devise.skip_trackable"] = true
  end

此代码更改了 Devise 在内部使用的哈希值,以决定是否更新它存储的值以跟踪用户上次活动的时间.具体来说,这是我们正在与之交互的设计代码(link):

This code changes a hash value that Devise uses internally to decide whether to update the value it stores to keep track of when the user last had activity. Specifically, this is the Devise code we're interacting with (link):

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 处理来自客户端的请求时,都会执行此代码.)

(Note that this code is executed every time that Rails processes a request from a client.)

我们对接近尾声的这些台词很感兴趣:

These lines near the end are interesting to us:

    unless env['devise.skip_trackable']
      warden.session(scope)['last_request_at'] = Time.now.utc
    end

这是在用户因不活动而注销之前重置"倒计时的代码.它仅在 env['devise.skip_trackable'] 不是 true 时才执行,因此我们需要在 Devise 处理用户请求之前更改该值.

This is the code that "resets" the countdown until the user is logged out due to inactivity. It's only executed if env['devise.skip_trackable'] is not true, so we need to change that value before Devise processes the user's request.

为此,我们告诉 Rails 在执行任何其他操作之前更改 env['devise.skip_trackable'] 的值.再次,从我的最终代码:

To do that, we tell Rails to change the value of env['devise.skip_trackable'] before it does anything else. Again, from my final code:

  prepend_before_action :skip_timeout, only: [:check_time_until_logout, :has_user_timed_out]
  def skip_timeout
    request.env["devise.skip_trackable"] = true
  end

<小时>

这点以上的所有内容都是我需要更改的内容才能回答我的问题.不过,我还需要进行一些其他更改才能使我的代码按我的意愿工作,因此我也会在此处记录它们.


Everything above this point is what I needed to change to answer my question. There were a couple other changes I needed to make to get my code to work as I wanted, though, so I'll document them here, too.

正确使用Timeoutable

我误读了关于 Timeoutable 模块的文档,所以我的问题中的代码还有一些其他问题.

I misread the documentation about the Timeoutable module, so my code in my question has a couple other problems.

首先,我的 check_time_until_logout 方法将始终返回相同的值.这是我操作的错误版本:

First, my check_time_until_logout method was going to always return the same value. This is the incorrect version of the action I had:

def check_time_until_logout
  @time_left = current_user.timeout_in
end

我认为 Timeoutable#timeout_in 会返回用户自动注销之前的时间量.相反,它返回 Devise 配置为在用户注销之前等待的时间量.我们需要计算用户离开自己的时间.

I thought that Timeoutable#timeout_in would return the amount of time until the user was automatically logged out. Instead, it returns the amount of time that Devise is configured to wait before logging users out. We need to calculate how much longer the user has left ourselves.

为了计算这个,我们需要知道用户上次有 Devise 识别的活动是什么时候.这段代码来自我们上面看到的 Devise 源代码,用于确定用户上次处于活动状态的时间:

To calculate this, we need to know when the user last had activity that Devise recognized. This code, from the Devise source we looked at above, determines when the user was last active:

    last_request_at = warden.session(scope)['last_request_at']

我们需要获取warden.session(scope)返回的对象的句柄.它看起来像Devise为我们提供的user_session哈希,就像句柄current_useruser_signed_in?,是那个对象.

We need to get a handle to the object returned by warden.session(scope). It looks like the user_session hash, which Devise provides for us like the handles current_user and user_signed_in?, is that object.

使用 user_session 哈希值并自己计算剩余时间,check_time_until_logout 方法变为

Using the user_session hash and calculating the time remaining ourselves, the check_time_until_logout method becomes

  def check_time_until_logout
    @time_left = Devise.timeout_in - (Time.now - user_session["last_request_at"]).round
  end

我也误读了 Timeoutable#timedout? 的文档.当您传入用户上次活动的时间时,它会检查用户是否已超时, 在您传入当前时间时.我们需要进行的更改很简单:我们需要在 user_session 哈希中传递时间,而不是传入 Time.now:

I also misread the documentation for Timeoutable#timedout?. It checks to see if the user has timed out when you pass in the time the user was last active, not when you pass in the current time. The change we need to make is straightforward: instead of passing in Time.now, we need to pass in the time in the user_session hash:

  def has_user_timed_out
    @has_timed_out = (!current_user) or (current_user.timedout? (user_session["last_request_at"]))
  end

一旦我进行了这三项更改,我的控制器就会按照我预期的方式运行.

Once I made these three changes, my controller acted the way I expected it to.

这篇关于设计 - 发出请求而不重置倒计时,直到用户因不活动而注销的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆