不活动/空闲后自动注销

时间:2013-01-29 07:41:21

标签: ruby-on-rails authentication devise

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

4 个答案:

答案 0 :(得分:24)

您应该使用Timeoutable模特特征。

  

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

     

选项

     

Timeoutable将以下选项添加到devise_for:

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

在您的模型中,您需要

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

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

答案 1 :(得分:11)

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

问题

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

在我的情况下,这是一个问题,因为我的网页包含用户敏感信息,如果用户忘记注销并且页面上没有执行任何操作,我不想无限期地显示这些信息。

解决方案

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

依赖关系

它在自述文件中没有说,但自动会话超时需要jquery-periodicalupdater才能工作。 This page包含以下原因: auto-session-timeout periodicalupdater explanation

配置

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

  1. 首先,我按照步骤here来自定义Devise会话控制器。仅供参考,我的 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分钟。这取代了Devise超时功能。

  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.rb 文件中的auto_session_timeout设置),则认证令牌将过期,用户在尝试登录时将收到无效的身份验证令牌错误。添加代码<%= auto_session_timeout_js %>将导致在身份验证令牌过期时刷新登录,从而防止发生此错误。

答案 2 :(得分:5)

使用Devise Gem:

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

我们可以执行自动退出:

使用gem“auto-session-timeout”

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

第4步:div找到它是否登录页面

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

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

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

答案 3 :(得分:0)

有一种没有提及的简单方法,不需要额外的宝石或依赖项。

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分钟标记处,倒数显示剩余的秒数,并可以单击按钮以保持登录状态。

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