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阶段的东西,似乎工作得相当好 – 这里有一些笔记