实现同账号只能有一个在线
in 后端 with 0 comment

实现同账号只能有一个在线

in 后端 with 0 comment

背景

做个练习/考试系统,要求同时只能在一个地方登录。

token:登陆凭证
userId:用户ID
userInfo:用户信息对象

原登录实现方式

目前我最常用的保持登录态方式,是在Redis中维护一个token : userInfo的键值对。

在用户登录的时候生成个唯一不重复的UUID作为token,以token : userInfo的形式存入Redis中。然后将token返回给前端,在需要认证身份的接口让前端将token放在header中的Authorization参数里。

但是这么做有一个问题,只能通过token找到用户信息,如果想反过来通过用户信息找到token,得遍历Redis,显然不现实。所以在登录的时候无法挤掉同账号的其他token

解决方案一

额外维护一个userId : token的键值对,这样登录的时候可以在获取到用户信息对象后,利用userId找到旧的token : userInfo并删除,然后新增新的token : userInfo。同时更新新userId : token为新token
此时维护一个用户登陆状态,需要userId : tokentoken : userInfo两个键值对。
不足之处:
假设一个账号同时在两个客户端登录,产生了并发问题,因为查询和删除旧token是两步操作不具备原子性,就有可能会出现同时设置userId : token键值对的情况,我们知道Redis操作Set key value的时候是会直接覆盖,所以总会有一个丢失。
加锁可以解决这个问题,但似乎会变得很复杂。

解决方案二

生成token时,利用userId做前缀,即userId_token,然后以userId_token : userInfo的形式存入Redis,每次登录前利用Rediskeys userId_*匹配到旧userId_token并删除掉。这种方式可以确保Redis中同个账号只有一份有效token
此时维护一个用户登陆状态,需要userId_uuid : token一个键值对。返回给前端的token应该为userId_token
不足之处:
在Redis数据量巨大的情况下,由于Redis时单线程,使用keys XXX匹配Key会导致阻塞,这点需要注意。
可以将用户登录的token单独存储在一个库里。

Responses