在Rails应用程序中实现"记住我"

时间:2020-03-05 18:37:21  来源:igfitidea点击:

我的Rails应用程序有一个带有"记住我"复选框的登录框。选中该复选框的用户即使关闭浏览器也应保持登录状态。我通过在用户会话中存储其ID来跟踪用户是否已登录。

但是会话在Rails中作为会话cookie实现,但不是持久性的。我可以使它们持久化:

class ApplicationController < ActionController::Base
  before_filter :update_session_expiration_date

  private

  def update_session_expiration_date
    options = ActionController::Base.session_options
    unless options[:session_expires]
      options[:session_expires] = 1.year.from_now
    end
  end
end

但这似乎是一种骇客,对于如此常见的功能而言令人惊讶。有什么更好的办法吗?

编辑

Gareth的答案相当不错,但是我仍然希望熟悉Rails 2的人给出答案(因为它是唯一的CookieSessionStore)。

解决方案:

我们几乎应该肯定不会延长会话cookie的寿命。

尽管没有专门讨论Rails,但本文还是花了一些时间来解释"记住我"的最佳实践。

总而言之,我们应该:

在用户表中添加一个额外的列以接受较大的随机值在客户端上设置将用户ID和随机值结合在一起的长寿命cookie当新的会话开始时,请检查id / value cookie的存在并进行身份验证新用户(如果他们匹配)。会话cookie设置为有效期(大约6个月)会话存储内部设置为登录的"到期日期" + 24小时用户ID Authenticated = true,因此我可以允许匿名用户进行烦恼(这并不危险,因为Cookie篡改保护的## 解决方案),我在应用程序控制器中添加了一个before_filter来检查会话的"过期时间"部分。在供应商/插件目录中包含一个文件,仅使用一行就可以在应用程序控制器中设置会话到期值

作者还建议使随机值无效,并在每次登录时重置cookie。我个人不喜欢那样,因为那样一来我们就无法保持登录两台计算机上的站点。我倾向于确保我的密码更改功能也重置随机值,从而锁定其他计算机上的会话。

最后要说的是,他提出的关于使某些功能(密码更改/电子邮件更改等)无法用于自动身份验证的会话的建议非常值得遵循,但在现实世界中很少见。

这是一个很好的关于创建30天持续会话的经验的写照。

警告:博客文章来自2006年

http://grahamglass.blogs.com/main/2006/05/rails_sessionsr.html

restful_authentication插件对此有一个很好的实现:

http://agilewebdevelopment.com/plugins/restful_authentication

请注意,我们不想保留他们的会话,而只想保留其身份。当他们返回网站时,我们将为他们创建一个新的会话。通常,我们只需为用户分配一个GUID,将其写入他们的cookie,然后在他们回来时使用它来查找他们。不要使用他们的登录名或者用户ID作为令牌,因为很容易猜到令牌,并允许狡猾的访问者劫持其他用户的帐户。

我花了一段时间思考这个问题,并得出了一些结论。默认情况下,Rails会话cookie是防篡改的,因此我们不必担心在客户端上修改cookie。

这是我所做的:

{分块}

当用户选中"记住我"框时,我只是将会话[:expireson]日期设置为登录+ 2周。因为Rails会话cookie是防篡改的,所以没有人可以窃取该cookie并永远保持登录状态或者冒充其他用户伪装。

我建议我们或者看一下具有此实现的RESTful_Authentication插件,或者只是切换实现以使用RESTful Authentication_plugin。在Railscasts中有关于如何使用此插件的很好的解释:

railscasts#67 restful_authentication

这是插件本身的链接

restful_authentication

这对我来说就像是一种魅力:

http://squarewheel.wordpress.com/2007/11/03/session-cookie-expiration-time-in-rails/

现在,我的CookieStore会话将在两周后到期,因此用户必须再次提交其登录凭据才能持续登录两周。

基本上,它很简单:

{分块}