在Rails 3中设置会话超时时间

48

这似乎很简单:我想让我的Rails Active Record会话在两分钟后超时。因此,两分钟后,我希望我的用户必须重新登录。

我只是在本地开发机器上运行rails server(即WebBrick)。

我知道这与config/initalizers/session_store.rb中的以下代码有关,但我认为我还没有完全搞清楚:

CodedOn::Application.config.session_store :active_record_store

CodedOn::Application.configure do
    config.action_controller.session = {:expire_after => 2.minutes}
end

这似乎无法工作,或者至少我的会话似乎没有超时。我找不到有关Rails 3的资料,以了解如何做到这一点,因为我知道从Rails 2.x开始有很多变化。

可以有人帮我吗?

6个回答

49

我认为你需要手动完成这个操作,因为Active Record Store并没有实现expire_after选项。所以在你的(我猜)before filter中,你应该这样做:

def authenticate
  if session[:logged_in]
    reset_session if session[:last_seen] < 2.minutes.ago
    session[:last_seen] = Time.now
  else
    ... authenticate
    session[:last_seen] = Time.now
  end
end

显然,这不是完整的,但它应该给你基本的思路。

更新:

看起来自Rails 2.3版本以来,功能就已经存在了。我在这里找到了相关的代码(链接)。这是AbstractStore,应该作为所有派生类的基类。因此,就像dadooda建议的那样,以下应该有效:

Some::Application.config.session_store :active_record_store, {
  expire_after: 24.hours,
}

1
这里有一个问题,就是它是如何工作的...比如说,当会话最初创建时调用了 before_filter - 即当用户首次进行身份验证时,session[:last_seen] 是如何再次被调用的呢?如果它是一个身份验证的 before_filter,那么它不是只被调用一次 - 在身份验证时吗?我想我试图在我的脑海中弄清楚的是,一旦会话被创建并且用户已登录,我如何检查 session[:last_seen],以便如果它超过 X 分钟,它会强制他们重新登录,而不是在会话最初创建时就这样做。希望这讲得通。 - marcamillion
1
我希望我理解了你的问题:HTTP是无状态的。因此,Web服务器不会看到任何两个请求之间的关系。但是有一些用例需要这种关系(例如身份验证)。在Rails中,会话哈希提供了这个功能。但是控制器必须在每个请求中读取会话哈希,以识别先前经过身份验证的用户。因此,“authenticate”-before过滤器在每个请求之前被调用。希望这可以帮助你。 - moritz
假设我从头开始编写了我的身份验证,那么您的答案是否基于我在每个控制器上显式调用before_filter,或者我必须在每个控制器上传递filter_resource_access才能强制执行它?我知道如果我使用devise或其他身份验证 gem,它会为我处理这些事情 - 但是考虑到我正在从头开始编写一个身份验证,这就是我问这些问题的原因。 - marcamillion
1
@marcamillion:这是因为按照惯例,您的控制器都继承自ApplicationController(例如class PostsController < ApplicationController),因此它们会继承before_filter声明。 - Luke Griffiths
@mosche 看起来不再是这样了,详见下面链接:https://dev59.com/42025IYBdhLWcg3wr4B6#14293073。 - Alex Fortuna
显示剩余2条评论

45

我用简单的方法实现了这个,你可以试一下:

在你的config/initializers/session_store.rb中只需要这样做:

Yourapp::Application.config.session_store :cookie_store, 
                                             :key => "_yourapp_session",
                                             :expire_after => 2.minutes

这对我来说很好用,希望你也能用。


2
同样适用于Rails 5.0.0。 - bevanb

4

你必须手动完成。这里有一个创建ActiveRecord会话类方法的示例。你可以使用Rufus-Scheduler和/或DelayedJob来定期调用它。

class Session < ActiveRecord::Base
  def self.sweep(time = 1.hour)
    if time.is_a?(String)
      time = time.split.inject { |count, unit| count.to_i.send(unit) }
    end

    delete_all "updated_at < '#{time.ago.to_s(:db)}' OR created_at < '#{2.days.ago.to_s(:db)}'"
  end
end

更多关于为什么这很重要的背景资料: http://guides.rubyonrails.org/security.html#session-expiry

4

mosch说:

我认为你需要手动完成此操作,因为活动记录存储不实现expire_after选项。

看起来现在情况已经不是这样了。 :expire_after 在Rails 3.2.11中对我有用:

Some::Application.config.session_store :active_record_store, {
  key: "some_session_id",
  domain: ".isp.com",
  expire_after: 24.hours,
}

每次向应用程序发送请求时,cookie会自动续订。会话仍然存在即使退出浏览器。

我使用上述技巧来在不同域之间“全局化”会话,以实现简单的SSO功能,目前似乎工作正常。


2
过期时间。
MAX_SESSION_TIME = 60 * 60

before_filter :prepare_session

def prepare_session

   if !session[:expiry_time].nil? and session[:expiry_time] < Time.now
      # Session has expired. Clear the current session.
      reset_session
   end

   # Assign a new expiry time, whether the session has expired or not.
   session[:expiry_time] = MAX_SESSION_TIME.seconds.from_now
   return true
end

-6
只需在您的模型中添加timeout_in方法即可解决问题:
  def timeout_in
    2.minutes
  end

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接