新手问题 系统使用 Devise 做的登录,有个需求 [同一帐号被另一台机器登录,本机器被强制退出登录],求大神指点一下

sanm1992 · September 04, 2018 · Last by w7938940 replied at September 07, 2018 · 3592 hits

公司系统用 devise 做的登录,有个需求【同一帐号被另一台机器登录,本机器被强制退出登录】,也就是一个账号只能登录同时登录一台电脑,本来觉着很简单,但自己鼓捣了半天也没什么好的方法,希望有经验的打大神指点一下🙏

我没有做过类似的功能,但我觉得可以这样实现:

  1. session 存储于 Redis
  2. 第一次登录为当前设备生成唯一码 uuid,存放在 cookies 及 session 中
  3. 每次请求判断 cookies 中的 uuid 及 session 中的 uuid 一致与否,若不一致自动登出
  4. 新 (设备) 登录重置 session 中的 uuid

以上仅仅提供一个思路,不一定是最好的解决方案,仅供参考

Reply to dfzy5566

感谢感谢! 我目前的思路是:在 users 表中增加 last_session_id 字段,用于存储上次登录生成的 session_id,登出的时候清空;然后每次登录之前检查 last_session_id 是否存在,如果存在,说明该用户已经登录,然后据此找到对应的 session 清空,但是我不知道怎么根据 session_id 找到对应的 session; 感觉原理和您给的思路差不多,但我对【自动登出】不会实操,尴尬了,看了半天 devise 源码,没看明白😭

Reply to sanm1992

不一定要从源码入手,尝试这样是否可行:

before_action authenticate_user!
def authenticate_user!
  super
  # your code
end
Reply to sanm1992

devise 有个 sign_out(user) 方法。

这个方法我尝试过,但是没有登出,我使用两个浏览器登录同一个账户测试的

Reply to dfzy5566

您这个不是用户身份验证的么,不太明白您的意思

Reply to sanm1992

我的意思是,你可以在验证用户后面写你自己的逻辑,比如判断当前用户的 session_id 是否与你 DB 中保存的一样,不一样就 sign_out(user),至于登出的 redirect,你可以覆盖after_sign_out_path_for这个方法来指定登出跳转,但如果你要做到实时性,就是一但新设备登录,老设备就自动登出,那你可能要借助长轮询(Long Polling)或 WebSocket 来实现。

Reply to dfzy5566

嗯,我的理解是:用户验证是发生在登录成功之后的,如果按照您的方法,退出的应该是当前机器上的这个用户,而不是之前机器登录的用户;还有就是,每次登录的 session_id 都不会一样的,判断是否一样貌似没有意义了。其实我现在的困惑可以转换为怎么根据 session_id 登出相应的用户。

我这个需求按照产品的意思貌似不需要实时,只用后台登出就好了。感谢感谢!🙏

Reply to sanm1992

嗯,所以一开始我就是建议你做 uuid 来判断唯一设备,不过我想了想 uuid 放 session 不太妥。你可以这样操作:

  1. 第一次登录生成 uuid,存储到 user 的 device_uuid 字段,并记录到 cookies 中
  2. 用户每次请求判断 cookies 带的 uuid 是否与用户表记录的 device_uuid 一致,不一致就登出
  3. 新设备登录时,更新用户表中的 device_uuid
Reply to dfzy5566

看上去您的这个方案是可行的,稍后尝试一下,真心感谢!

Reply to Rei

哇哇,大神终于出现了,您的方法我会一一尝试的,回复这么仔细,对于一个新手来说是真的感谢🙏 🙏 🙏

Reply to sanm1992

十楼方法是增加一个过滤器判断额外的 token,不改变 devise 内部,原理一样但更安全。

觉着楼主记录最新的 session_id,行的通的。

通过旧的 session_id 找用户,这种后端主动登出,需要后端向前端交互,实时性高的话需要 websocket。

不过如果实时性要求不高的话,你可以通过在过滤器中判断只要与新的 session 不一致,就 sign_out,做这种被动处理。

Reply to flydragon

嗯,我一开始老想着清除对应的 session,苦于没有办法找到对应的 session,而没有想到加过滤器来判断是否登陆,然后登出,思路不够开阔

reset token,先登陆的用户自然就掉线了。。

我用 jwt 可以把 token 的 iat 放在 token 里面,服务端保存最新的 iat,客户端请求的 iat 若小于服务端的 iat 就返回 401

You need to Sign in before reply, if you don't have an account, please Sign up first.