Ruby on rails 设计每次限制每个用户一个会话

Ruby on rails 设计每次限制每个用户一个会话,ruby-on-rails,ruby-on-rails-3,session,devise,Ruby On Rails,Ruby On Rails 3,Session,Devise,我的应用程序使用Rails 3.0.4和Desive 1.1.7 我正在寻找一种防止用户共享帐户的方法,因为该应用程序是基于订阅的服务。我已经搜索了一个多星期,但我仍然不知道如何实现解决方案。我希望有人已经实施了一个解决方案,可以为我指出正确的方向 解决方案(感谢大家的回答和见解!) 在应用程序控制器中.rb before_filter :check_concurrent_session def check_concurrent_session if is_already_logged_i

我的应用程序使用Rails 3.0.4和Desive 1.1.7

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

解决方案(感谢大家的回答和见解!)

在应用程序控制器中.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
在覆盖设计会话控制器的会话控制器中:

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

跟踪每个用户使用的uniq IP。时不时地,对这些IP进行分析——如果一个帐户同时从不同国家的不同ISP登录,那么共享将是显而易见的。注意,仅仅有一个不同的IP是不足以考虑它共享的-一些ISP使用循环代理,所以每个命中必须是一个不同的IP。

< P>你不能这样做。< /P>
  • 您可以控制用户的IP地址,这样就可以防止用户同时出现在两个IP上。您可以绑定登录和IP。您可以尝试通过IP检查城市和其他地理位置数据以阻止用户
  • 您可以设置cookies来控制其他内容
但这些都不能保证只有一个用户使用这个登录,来自世界各地的105个IP不属于唯一一个使用代理或其他方式的用户

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

UPD

然而,我要问的是如何限制多个用户同时使用同一个帐户,我认为这应该是可能的

因此,您可以存储一些令牌,其中将包含一些加密数据:IP+保密字符串+用户代理+用户浏览器版本+用户操作系统+任何其他个人信息:
加密(IP+“某些保密字符串”+request.user\u agent+…)
。然后,您可以使用该令牌设置会话或cookie。对于每个请求,您都可以获取它:如果用户相同?他是否使用相同的浏览器以及来自相同操作系统的相同浏览器版本等


您还可以使用动态令牌:您可以在每个请求中更改令牌,这样每个会话中只有一个用户可以使用系统,因为每个请求令牌都将被更改,另一个用户将在其令牌过期时注销。

虽然您无法可靠地阻止用户共享帐户,但您可以做什么(我认为)防止多个用户同时登录到同一帐户。不确定这是否足以满足您的业务模式,但它确实解决了其他答案中讨论的许多问题。我已经实现了一些目前处于测试阶段的东西,并且似乎运行得相当好-有一些注释

关于在Desive中实际实现它,请将其添加到您的User.rb模型中。 类似这样的东西会自动将它们注销(未经测试)

这种宝石效果很好:

添加到文件

gem 'devise-security'
捆绑安装后

rails generate devise_security:install
然后跑

rails g migration AddSessionLimitableToUsers unique_session_id
rake db:migrate
编辑迁移文件

class AddSessionLimitableToUsers < ActiveRecord::Migration
  def change
    add_column :users, :unique_session_id, :string, limit: 20
  end
end
编辑你的app/models/user.rb文件

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

完成了。现在,从另一个浏览器登录将终止以前的任何会话。gem实际通知用户,他将在登录之前终止当前会话。

我发现原始帖子中的解决方案对我不太有效。我希望第一个用户注销,并显示一个登录页面。另外,
注销和重定向(当前用户)
方法似乎并不像我预期的那样工作。在该解决方案中使用SessionsController覆盖,我将其修改为使用WebSocket,如下所示:

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
确保所有网页都订阅了注销频道,并将其绑定到相同的
注销\订阅\地址
操作。在我的应用程序中,每个页面都有一个“注销”按钮,该按钮通过Desive会话销毁操作注销客户端。当网页中触发websocket响应时,它只需单击此按钮即可调用注销逻辑,并向第一个用户显示登录页面

此解决方案也不需要
skip_before_filter:check_concurrent_session
和model
login_token
,因为它会触发强制注销而不受影响


作为记录,
design_security_extension
似乎也提供了执行此操作的功能。它还向第一个用户发出了一个适当的警报,警告他们发生了什么事情(我还不知道怎么做)。

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

routes.rb

  devise_for :users, :controllers => { :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
  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\u token的迁移添加字符串字段

这会覆盖默认的Desive会话控制器,但也会从中继承。在新会话上,将创建登录会话令牌并将其存储在用户模型上的
login\u token
中。在应用程序控制器中,我们调用
check\u concurrent\u session
,它在调用
duplicate\u session?
函数后注销并重定向
当前用户


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

我更担心的是用户在组织内部共享帐户,而不是在国家之间共享帐户。如果同一网络上有多人共享帐户,uniq IP解决方案是否可行?可能,除非他们有NAT网关,这会使所有用户都出现在一个(或很少几个)IP下。我同意共享帐户是不可能停止的。不过,我想问的是,如何限制多个用户同时使用同一个帐户,我认为这应该是可能的。谢谢你更新fl00rs。我现在正在使用一个简单的令牌,直到我让它工作,但我同意
  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