Ruby-on-rails 设计每个用户一次限制一个会话
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/7068919/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Devise limit one session per user at a time
提问by John
My app is using Rails 3.0.4 and Devise 1.1.7.
我的应用程序使用 Rails 3.0.4 和 Devise 1.1.7。
I'm looking for a way to prevent users from sharing accounts as the app is a subscription based service. I've been searching for over a week, and I still don't know how to implement a solution. I'm hoping someone has implemented a solution and can point me in the right direction.
我正在寻找一种方法来防止用户共享帐户,因为该应用程序是基于订阅的服务。找了一个多星期了,还是不知道怎么实现。我希望有人已经实施了一个解决方案,并且可以为我指明正确的方向。
Solution(Thank you everyone for your answers and insight!)
解决方案(感谢大家的回答和见解!)
In application controller.rb
在应用程序控制器.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
In session_controller that overrides Devise Sessions controller:
在覆盖设计会话控制器的 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
In migration AddLoginTokenToUsers
在迁移中 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
采纳答案by fl00r
You can't do it.
你不能这样做。
- You can control IP addresses of user, so you can prevent presence of user from two IP at a time. ANd you can bind login and IP. You can try to check cities and other geolocation data through IP to block user.
- You can set cookies to control something else.
- 您可以控制用户的 IP 地址,因此您可以防止用户同时来自两个 IP。并且您可以绑定登录名和IP。您可以尝试通过 IP 检查城市和其他地理位置数据以阻止用户。
- 您可以设置 cookie 来控制其他内容。
But none of this will guarantee that only one user uses this login, and that those 105 IP from all over the world doesn't belong to only one unique user, which uses Proxy or whatever.
但这都不能保证只有一个用户使用此登录名,并且来自世界各地的 105 个 IP 不只属于一个使用代理或其他方式的唯一用户。
And the last: you never need this in the Internet.
最后一点:您在 Internet 中永远不需要这个。
UPD
UPD
However, what I'm asking is about limiting multiple users from using the same account simultaneously which I feel should be possible
但是,我要问的是限制多个用户同时使用同一个帐户,我认为这应该是可能的
So you can store some token, that will contain some encrypted data: IP + secret string + user agent + user browser version + user OS + any other personal info: encrypt(IP + "some secret string" + request.user_agent + ...). And then you can set a session or cookie with that token. And with each request you can fetch it: if user is the same? Is he using the same browser and the same browser version from the same OS etc.
因此,您可以存储一些令牌,其中将包含一些加密数据:IP + 秘密字符串 + 用户代理 + 用户浏览器版本 + 用户操作系统 + 任何其他个人信息:encrypt(IP + "some secret string" + request.user_agent + ...). 然后您可以使用该令牌设置会话或 cookie。对于每个请求,您都可以获取它:如果用户是相同的?他是否使用来自同一操作系统等的相同浏览器和相同浏览器版本?
Also you can use dynamic tokens: you change token each request, so only one user could use system per session, because each request token will be changed, another user will be logged out as far as his token will be expired.
您也可以使用动态令牌:您更改每个请求的令牌,因此每个会话只有一个用户可以使用系统,因为每个请求令牌都会更改,只要他的令牌过期,另一个用户将被注销。
回答by scarver2
This gem works well: https://github.com/devise-security/devise-security
这个 gem 运行良好:https: //github.com/devise-security/devise-security
Add to Gemfile
添加到 Gemfile
gem 'devise-security'
after bundle install
捆绑安装后
rails generate devise_security:install
Then run
然后运行
rails g migration AddSessionLimitableToUsers unique_session_id
Edit the migration file
编辑迁移文件
class AddSessionLimitableToUsers < ActiveRecord::Migration
def change
add_column :users, :unique_session_id, :string, limit: 20
end
end
Then run
然后运行
rake db:migrate
Edit your app/models/user.rb file
编辑您的 app/models/user.rb 文件
class User < ActiveRecord::Base
devise :session_limitable # other devise options
... rest of file ...
end
Done. Now logging in from another browser will kill any previous sessions. The gem actual notifies the user that he is about to kill a current session before logging in.
完毕。现在从另一个浏览器登录将终止任何以前的会话。gem 实际通知用户他将在登录前终止当前会话。
回答by nulltek
This is how I solved the duplicate session problem.
这就是我解决重复会话问题的方法。
routes.rb
路由文件
devise_for :users, :controllers => { :sessions => "my_sessions" }
my_sessions controller
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
User modelAdd a string field via a migration named login_token
用户模型通过名为的迁移添加字符串字段login_token
This overrides the default Devise Session controller but inherits from it as well. On a new session a login session token is created and stored in login_tokenon the User model. In the application controller we call check_concurrent_sessionwhich signs out and redirects the current_userafter calling the duplicate_session?function.
这会覆盖默认的设计会话控制器,但也继承自它。在新会话中,创建登录会话令牌并将其存储在login_token用户模型中。在应用程序控制器中,我们调用check_concurrent_sessionwhich 注销并current_user在调用该duplicate_session?函数后重定向 the 。
It's not the cleanest way to go about it, but it definitely works.
这不是最干净的方法,但它绝对有效。
回答by Dex
As far as actually implementing it in Devise, add this to your User.rb model. Something like this will log them out automatically (untested).
至于在设计中实际实现它,请将其添加到您的 User.rb 模型中。这样的事情会自动将它们注销(未经测试)。
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
回答by davidm
I found that the solution in the original posting did not quite work for me. I wanted the first user to be logged out and a log-in page presented. Also, the sign_out_and_redirect(current_user)method does not seem to work the way I would expect. Using the SessionsController override in that solution I modified it to use websockets as follows:
我发现原始帖子中的解决方案对我来说不太适用。我希望第一个用户注销并显示登录页面。此外,该sign_out_and_redirect(current_user)方法似乎并不像我期望的那样工作。在该解决方案中使用 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
Make sure that all web pages subscribe to the signout channel and bind it to the same logout_subscribe_addressaction. In my application, each page also has a 'sign out' button, which signs out the client via the devise session Destroy action. When the websocket response is triggered in the web page, it simply clicks this button - the signout logic is invoked and the first user is presented with the sign in page.
确保所有网页都订阅了退出频道并将其绑定到相同的logout_subscribe_address操作。在我的应用程序中,每个页面还有一个“退出”按钮,它通过设计会话销毁操作退出客户端。当 web 页面中的 websocket 响应被触发时,它只需点击这个按钮 - 退出逻辑被调用,第一个用户会看到登录页面。
This solution also does not require the skip_before_filter :check_concurrent_sessionand the model login_tokensince it triggers the forced logout without prejudice.
此解决方案也不需要skip_before_filter :check_concurrent_session和 模型,login_token因为它会在不带偏见的情况下触发强制注销。
For the record, the devise_security_extensionappears to provide the functionality to do this as well. It also puts up an appropriate alert warning the first user about what has happened (I haven't figured out how to do that yet).
为了记录,devise_security_extension似乎也提供了执行此操作的功能。它还向第一个用户发出适当的警报警告发生了什么(我还没有想出如何做到这一点)。
回答by chrispanda
While you can't reliably prevent users from sharing an account, what you can do (I think) is prevent more than one user being logged on at the same time to the same account. Not sure if this is sufficient for your business model, but it does get around a lot of the problems discussed in the other answers. I've implemented something that is currently in beta and seems to work reasonably well - there are some notes here
虽然您无法可靠地阻止用户共享帐户,但您可以做的(我认为)是防止多个用户同时登录到同一个帐户。不确定这对您的商业模式是否足够,但它确实解决了其他答案中讨论的许多问题。我已经实现的东西,目前处于测试阶段,似乎工作相当好-有一些注意事项这里
回答by Marc B
Keep track of uniq IPs used per user. Now and then, run an analysis on those IPs - sharing would be obvious if a single account has simultaneous logins from different ISPs in different countries. Note that simply having a different IP is not sufficient grounds to consider it shared - some ISPs use round-robin proxies, so each hit would necessarily be a different IP.
跟踪每个用户使用的 uniq IP。时不时地对这些 IP 进行分析——如果一个帐户同时从不同国家的不同 ISP 登录,共享将是显而易见的。请注意,仅仅拥有不同的 IP 不足以将其视为共享的理由 - 一些 ISP 使用循环代理,因此每次命中必然是不同的 IP。

