devise一次限制每个用户一个会话

我的应用程序正在使用Rails 3.0.4和Devise 1.1.7。

我正在寻找一种方法来阻止用户共享帐户,因为该应用程序是基于订阅的服务。 我一直在寻找一个多星期,但我仍然不知道如何实施解决scheme。 我希望有人已经实施了一个解决scheme,并指出我在正确的方向。

解决scheme (谢谢大家的答复和见解!)

在应用程序controller.rb

before_filter :check_concurrent_session def check_concurrent_session if is_already_logged_in? sign_out_and_redirect(current_user) end end def is_already_logged_in? current_user && !(session[:token] == current_user.login_token) end 

在重写Devise Sessions控制器的session_controller中:

 skip_before_filter :check_concurrent_session def create super set_login_token end private def set_login_token token = Devise.friendly_token session[:token] = token current_user.login_token = token current_user.save end 

在迁移AddLoginTokenToUsers

 def self.up change_table "users" do |t| t.string "login_token" end end def self.down change_table "users" do |t| t.remove "login_token" end end 

你不能这样做。

  • 您可以控制用户的IP地址,因此您可以一次阻止来自两个IP的用户的存在。 你可以绑定login和IP。 您可以尝试通过IP检查城市和其他地理位置数据来阻止用户。
  • 你可以设置cookie来控制别的东西。

但是,这些都不能保证只有一个用户使用这个login,而来自世界各地的那些IP不仅仅属于一个唯一的用户,它使用了代理或者其他什么。

最后一点:你永远不需要在互联网上。

UPD

然而,我问的是限制多个用户同时使用同一个帐户,我觉得应该是可能的

所以你可以存储一些令牌,这将包含一些encryption的数据:IP +秘密string+用户代理+用户浏览器版本+用户操作系统+任何其他个人信息: encrypt(IP + "some secret string" + request.user_agent + ...) 。 然后,您可以使用该令牌设置会话或cookie。 并且每个请求你可以获取它:如果用户是一样的? 他是否使用相同的浏览器和相同的浏览器版本从相同的操作系统等

您也可以使用dynamic令牌:每个请求都会更改令牌,因此只有一个用户可以使用每个会话的系统,因为每个请求令牌都将被更改,另一个用户将被注销,直到他的令牌过期。

这个gem运作良好: https : //github.com/phatworx/devise_security_extension

添加到Gemfile

 gem 'devise_security_extension' 

捆绑安装后

 rails g devise_security_extension:install 

然后运行

 rails g migration AddSessionLimitableToUsers unique_session_id 

编辑迁移文件

 class AddSessionLimitableToUsers < ActiveRecord::Migration def change add_column :users, :unique_session_id, :string, limit: 20 end end 

然后运行

 rake db:migrate 

编辑你的应用/模型/ user.rb文件

 class User < ActiveRecord::Base devise :session_limitable # other devise options ... rest of file ... end 

完成。 现在从另一个浏览器login会杀死任何以前的会话。 gem实际上通知用户在login之前他即将杀死当前会话。

至于在devise中实际执行它,将此添加到您的User.rb模型。 像这样的东西会自动注销(未经testing)。

  def token_valid? # Use fl00rs method of setting the token session[:token] == cookies[:token] end ## Monkey Patch Devise methods ## def active_for_authentication? super && token_valid? end def inactive_message token_valid? ? super : "You are sharing your account." end 

这就是我解决重复会话问题的方法。

的routes.rb

  devise_for :users, :controllers => { :sessions => "my_sessions" } 

my_sessions控制器

 class MySessionsController < Devise::SessionsController skip_before_filter :check_concurrent_session def create super set_login_token end private def set_login_token token = Devise.friendly_token session[:token] = token current_user.login_token = token current_user.save(validate: false) end end 

application_controller

  def check_concurrent_session if duplicate_session? sign_out_and_redirect(current_user) flash[:notice] = "Duplicate Login Detected" end end def duplicate_session? user_signed_in? && (current_user.login_token != session[:token]) end 

用户模型通过名为login_token的迁移添加string字段

这覆盖默认的devise会话控制器,但也inheritance它。 在新会话中,login会话令牌被创build并存储在用户模型的login_token 。 在应用程序控制器中,我们调用check_concurrent_session ,它在调用duplicate_session?之后注销并redirectcurrent_user duplicate_session? function。

这不是最干净的方式,但它确实有效。

我发现原来发布的解决scheme并不适合我。 我希望第一个用户注销,并提供一个login页面。 此外, sign_out_and_redirect(current_user)方法似乎并不按我期望的方式工作。 在该解决scheme中使用SessionsController覆盖,我修改它以使用websockets,如下所示:

 def create super force_logout end private def force_logout logout_subscribe_address = "signout_subscribe_response_#{current_user[:id]}" logout_subscribe_resp = {:message => "#{logout_subscribe_address }: #{current_user[:email]} signed out."} WebsocketRails[:signout_subscribe].trigger(signout_subscribe_address, signout_subscribe_resp) end end 

确保所有网页都订阅了注销频道,并将其绑定到相同的logout_subscribe_address操作。 在我的应用程序中,每个页面还有一个“注销”button,通过devise会话“销毁”操作注销客户端。 当web页面中的websocket响应被触发时,只需点击此button – 注销逻辑被调用,第一个用户被显示login页面。

此解决scheme也不需要skip_before_filter :check_concurrent_session和模型login_token因为它会在不影响login_token情况下触发强制注销。

为了logging, devise_security_extension似乎也提供了这样做的function。 它还提出了一个适当的警报警告第一个用户发生了什么事情(我还没有想出如何做到这一点)。

跟踪每个用户使用的uniq IP。 现在,对这些IP进行分析 – 如果一个帐户同时login来自不同国家的不同ISP,共享将是显而易见的。 请注意,简单地拥有不同的IP并不足以将其视为共享 – 某些ISP使用循环代理,因此每个命中都必须是不同的IP。

虽然您无法可靠地阻止用户共享帐户,但是您可以执行的操作(我认为)可以防止多个用户同时login到同一个帐户。 不知道这是否足以满足您的商业模式,但它确实解决了其他答案中讨论的许多问题。 我已经实现了目前处于testing阶段的东西,似乎工作得相当好 – 这里有一些笔记