23

如何在 Rails 应用程序中设置如果任何用户空闲 30 分钟或特定时间段,他应该自动退出。任何人都可以给出任何解决方案。我正在使用设计进行身份验证。任何帮助表示赞赏。

4

4 回答 4

27

您应该使用Timeoutable模型特征。

Timeoutable 负责判断用户会话是否已经过期。当会话在配置的时间后过期时,将再次要求用户提供凭据,这意味着他/她将被重定向到登录页面。

选项

Timeoutable 为 devise_for 添加了以下选项:

  • +timeout_in+:无活动用户会话超时的时间间隔。

在您的模型中,您需要

devise :timeoutable
# along with :database_authenticatable, :registerable and other things.

另外,看一下config/initializers/devise.rb,您可以在那里配置超时值。

于 2013-01-29T07:42:52.973 回答
18

我知道这个问题已经得到解答,但我想我也会提供我的解决方案,因为在我的情况下,我正在寻找比 Devise 的可超时功能所能提供的更多的功能。非常感谢@Sergio Tulentsev 在他的回答的评论部分为我提供了有用的解释和想法!

问题

因为 Devise 在服务器端而不是客户端运行,所以当身份验证令牌超时时,客户端不会知道超时,直到用户执行调用 Rails 控制器的操作。这意味着用户不会在超时时重定向到登录页面,直到他们执行调用 Rails 控制器的操作。

就我而言,这是一个问题,因为我的网页包含用户敏感信息,如果用户忘记注销并且未在页面上执行任何操作,我不希望无限期地显示这些信息。

解决方案

我安装了 gem auto-session-timeout,它将代码添加到客户端以定期检查身份验证令牌是否已过期。

依赖项

自述文件中没有说,但自动会话超时需要jquery-periodicalupdater才能工作。 此页面包含原因: auto-session-timeout periodicalupdater 解释

配置

以下是我为了让自动会话超时与 Devise 一起工作而采取的步骤:

  1. 首先,我按照此处的步骤自定义设计会话控制器。仅供参考,我的config/routes.rb文件是按以下方式设置的:

    Myapp::Application.routes.draw do
      devise_for :users, controllers: { sessions: "users/sessions" }
      #other routes
    end
    
  2. app/controllers/users/sessions_controller.rb,我有以下代码:

    class Users::SessionsController < Devise::SessionsController
      layout 'login' #specifies that the template app/views/layouts/login.html.erb should be used instead of app/views/layouts/application.html.erb for the login page
    
      #configure auto_session_timeout
      def active
        render_session_status
      end
    
      def timeout
        flash[:notice] = "Your session has timed out."
        redirect_to "/users/sign_in"
      end
    end
    
  3. app/controllers/application_controller.rb,我有以下代码:

    class ApplicationController < ActionController::Base
      # Prevent CSRF attacks by raising an exception.
      # For APIs, you may want to use :null_session instead.
      protect_from_forgery with: :exception
      before_action :authenticate_user!
      auto_session_timeout 30.minutes
    
    end
    

    请注意,我们使用 auto_session_timeout 将身份验证令牌到期时间设置为 30 分钟。这取代了设计超时功能。

  4. 在我的应用程序中,我有两个布局模板 - 一个用于用户登录时看到的所有页面(app/views/layouts/application.html.erb),一个仅用于登录屏幕(app/views /layouts/login.html.erb)。在这两个文件中,我在 html<body>元素中添加了以下行:

    <%= auto_session_timeout_js %>
    

    此代码将生成 Javascript,每 60 秒检查一次身份验证令牌的状态(此时间间隔是可配置的)。如果令牌已超时,Javascript 代码将调用app/controllers/users/sessions_controller.rb文件timeout中的方法。

    请注意,我已在app/views/layouts/login.html.erb页面上包含此代码。这样做的原因是因为如果登录页面超过 30 分钟没有活动(或者无论application_controller.rbauto_session_timeout文件中的设置如何),那么身份验证令牌将过期,并且用户将收到一个无效的身份验证令牌尝试登录时出错。添加代码将导致身份验证令牌过期时刷新登录,从而防止出现此错误。<%= auto_session_timeout_js %>

于 2016-04-07T16:39:16.727 回答
7

使用设计宝石:

我们可以使用设计 gem 的内置功能,但它不会在超时后自动重定向到登录页面,重定向将在我们执行任何操作后完成。

我们可以执行自动注销:

通过使用 gem “自动会话超时”

https://github.com/pelargir/auto-session-timeout

使用这个 gem 的缺点是,如果用户在超时时间之前只输入(执行按键事件),它将自动注销。

我们可以通过使用 Javascript 来克服这个缺点:

第 1 步:定义路线

get 'application/session_time'

第 2 步:JavaScript 将包含

$(document).ready(function(){
if($("#user_logged").is(":visible") == true )
{
    $(document).on( "keypress keydown", function () {
        console.log("hello");
        $.ajax({
            type:"GET",
            url:"/application/session_time",
            dataType:"html",
        });
    });
}
});

第 3 步:应用程序控制器将包含:

@session_time = 5.minute
auto_session_timeout @session_time
def session_time
  @session_time = 5.minute
end

Step 4: div 判断是否为登录页面

<% if user_signed_in? %>
  <div id="user_logged"></div>
<% end %>

保留空白 div 是因为我们必须仅在用户登录时加载 JavaScript,因此而不是查找当前用户是否为 nil。

我已经完成了空白 div,如果用户登录,它将可用,因此在 JavaScript 的开头检查 div_id "user_loged" 是否存在。

于 2017-04-18T13:39:34.613 回答
2

有一种未提及的简单方法来处理此问题,不需要额外的 gem 或依赖项。

initializers/devise.rb你已经设置config.timeout_in = 30.minutes并添加:timeoutable到你的模型中。当用户登录时,在页面加载时触发以下 javascript:

setAccurateTimeout(() => {
  window.location.reload();
}, 30 * 60 * 1000);  // minutes (from devise setting) * sec * ms

function setAccurateTimeout(callback, length) { 
  // adjust any discrepencies every 5s
  let speed = 5000,                
      steps = length / speed,                           
      count = 0,
      start = new Date().getTime();

  function instance() {
    if (count++ == steps) {
      callback();
    } else {
      // console.log(`step ${count} of ${steps}, time passed ${count * speed}ms of ${length}ms`)
      let diff = (new Date().getTime() - start) - (count * speed);
      // console.log(`accuracy diff ${diff}ms, adjusted interval: ${speed - diff}ms`);
      window.setTimeout(instance, (speed - diff));
    }
  }
  window.setTimeout(instance, speed);
}

可能会使用常规setTimeout,即使随着时间的推移它会由于 CPU 使用率而引入不准确性。它可能会比预期的稍晚触发注销重新加载。

由于在客户端的javascript之前初始化,服务器将在此完成之前稍微终止会话。当页面重新加载时,浏览器将最终出现在登录屏幕上。这种方法还可以很容易地提前触发警告模式,例如在 2 分钟标记处带有显示剩余秒数的倒计时和可以单击以保持登录状态的按钮。

额外提示:在“保持登录”按钮上,将 url 设置为您的一个页面并添加data-remote='true'属性。单击此按钮时,将触发对服务器的请求,而无需重新加载用户所在的页面,从而满足活动要求并重置设备的超时,而无需重新加载或在任何地方导航。取消任何程序化页面重新加载,然后重新启动主超时。

于 2020-08-01T00:57:33.757 回答