【任务10】注册与登录:不允许多个设备同时在线
1、用文字简单描述一下你的处理逻辑
2、思考题:如果我想实现电脑/手机可以同时登录,类似于微信可以手机,电脑同时在线,但是无法两个手机/电脑同时在线,这种可以怎么做呢?
本问题对应的项目链接:从0设计一个web项目
PS:每次在最后,都要给出你的GitHub链接哦,方便我打开
1、用文字简单描述一下你的处理逻辑
2、思考题:如果我想实现电脑/手机可以同时登录,类似于微信可以手机,电脑同时在线,但是无法两个手机/电脑同时在线,这种可以怎么做呢?
本问题对应的项目链接:从0设计一个web项目
PS:每次在最后,都要给出你的GitHub链接哦,方便我打开
共以下 6 个回答
我的实现逻辑是用redis存储随机数,key为用户id,只保留最后一个登录的用户生成的token,在用户登录时进行判断,如果redis里面存了该用户的token,我们就将其删除,然后设置一个新的token值,将其挤下线;
https://github.com/CurryisCoat/community/tree/dev
我的实现逻辑和前面的Seconds大佬很像,同样是用redis存储随机数-用户名和用户名-随机数。不同的点在于,我的随机数-用户名是多对一的关系。也就是说,记录多个session随机数的值,当你在其他地方的登陆下线后,你此前正常登陆的session随机数还是可以使用的。如果是一对一的关系的话,在其他地方登陆不管下不下线,此前保留一天的session都不能够使用了。用户名-随机数这一对表示的当前登陆的session随机数,设置5分钟过期,也就是5分钟后自动下线,发送请求时会刷新过期时间。
同样我的代码也存在key值可能为中文的问题,后面可能考虑换成用户id来改进。
github地址:https://github.com/lei342345795/FromZerotoExpert
你的意思是说,你保存了多个随机数,是为了让「最新登录的人下线之后」,其他地方登录的人的session还能正常使用?
登录态逻辑
v1.0(Cookie + Session)
v2.0(Redis + ThreadLocal)
使用 Redis 解决 Session 无法共享的问题,使用 ThreadLocal 模拟本地 Session 存储
至此,还不具备对多设备同时在线进行限制的能力
解决方案
维护 用户id:token 的键值对,其中,token是唯一的,只保存最后一个登录的用户生成的 token,称为在线 token
用户登录时,会生成token,如果存在旧的在线 token(之前有人在线),就根据用户 id 获取该 token,并删除该 token 存储的用户信息(下线)
这样下一次前面的用户校验登录态时就获取不到用户信息了,被视为已下线。
如何实现多类型设备登录,而限制多台同一类型设备登录?
修改在线token的结构,改为 hash 结构,维护多种类型设备在线token,每种设备仍保持唯一
需要打造能鉴别设备类型的能力。
github地址(点击即可)
GitHub地址:https://github.com/shuaishuaixi03/FromZerotoExpert/tree/dev
每次用户登录时都会更新这个唯一登录凭证,拦截器中拦截本地的登录凭证和 redis 记录的凭证不同的请求。如果同时有两用户登录,则后面用户的唯一登录凭证是最新的,前一个人只需在下一次操作时用拦截器中拦截即可。
遇到的问题:
这些问题等国庆放假我再好好想想。
服务器链接:http://124.223.184.187:8088/FromZerotoExpert
Github仓库链接:https://github.com/jiangyklala/FromZerotoExpert/tree/dev
一、仅后者在线的处理逻辑
1.前置实现(怎样设置的登录态):
当用户第一次登录成功时,我们随机生成一个sessionId(雪花算法生成的唯一字符),用session进行存储,键为”user”,值为刚刚生成的sessionId,然后在reids数据库中存入该用户的登录信息,在这里我们存储两个键值对,一个是键为username(真实的用户名)值为sessionId的键值对,另一个是键为sessionId值为username(真实的用户名)的键值对。这样我们就可以将用户的信息存储到redis数据库中,方便调用和查询。
2.仅后者在线
当用户第二次登录时,首先服务器会查询redis数据库是否含有该用户名username-sessionId的键值对(若是第一次登录或者登录已过期,则不会查到,直接跳过此判断),若查询到确实存在此键值对,则说明不是第一次登录,于是把已经存储在redis数据库中的uername-sessionId和sessionId-username键值对设置过期,并生成一个新的sessionId存储在session中,在session中存储的键依然是”user”,存储的值是新生成的sessionId,这样就能把之前session中存储的sessionId覆盖掉了。并在redis数据库中保存新生成的sessionId-username和username-sessionId两个键值对。这样的话对于登录的前者,在session中存储的sessionId已经被覆盖了,在redis数据库存储的之前用户信息也被删除了,所以前者的登录会在拦截器部分被当作未登录处理。于是就简单的实现了仅后者在线的逻辑。
二、思考题(手机/PC端可以同时登录)
以我目前的实现方式,具体的设计思路如下:
1.登录态的设置
和上述的过程的实现方式类似,只不过当用户第一次登录时,首先需要获取user-agent进行用户登录方式的判断,通过利用request.getHeader(user-agent)的方法判断该用户的登录方式,若登录的是手机端,则在session中存储的内容为user:phone-sessionIdPhone,若为PC端,则在session中存储的内容为user:pc-sessionIdPc,并在redis数据库中存储对应的信息,比如当用户是手机端时,redis数据库以该用户名username+:phone拼接而成的字符串和sessionIdPhone互为键值对进行存储;当用户是PC端时,redis数据库以该用户名username+:pc拼接而成的字符串和sessionIdPc互为键值对进行存储。
2.仅允许一个手机/电脑同时在线
当用户第二次登录时,先进行request.getHeader(user-agent)的方法判断该用户是手机端还是PC端,若为手机端登录则在redis数据库中查找是否存在username:phone-sessionId的键值对存在,若存在则证明之前已经用手机端登陆过了,所以重新生成sessionIdPhone,并将之前redis数据库存储的username:phone-sessionIdPhone和sessionIdPhone-username:phone进行更新,同样地,若是PC端也是如此。总之就是比较折腾和麻烦,可能换一种数据结构会比较好一点,比如利用redis中的key-hash的结构进行设计可能扩展性会强一点,不过这边还没细想,因为不知道自己这样的做法是不是符合规范,只是先想了这么多,只能说是能简单完成了基本需求吧。
gitHub地址:https://github.com/Second6s9/FromZerotoExpert/tree/dev
gitHub防覆盖地址:https://github.com/Second6s9/FromZerotoExpert/tree/day10
服务器地址:http://123.57.142.33:8088/FromZerotoExpert/
关于一人登录的,这样实现好像也还行,不过就是有个需要注意的点,就是用户名很多是中文名,中文的话,不是特别适合作为 key。
是的帅地哥,这里我是做过一些中文用户名的测试案例的,功能上其实没太大问题,不过存中文key会发生中文字符转义的情况,我其实也发现了中文key确实不大合适,所以这里后面我会进行修改的。因为我之前没怎么接触过限制登录个数的这种需求的,所以我就按照自己想的方式,自己设计了这一套处理的逻辑,写着顺手就直接敲出来了,可能看上去弊端会很多吧。这里我后面会查一些相关的项目或者博客,然后重新设计一套比较标准的处理逻辑的。
是滴,先自己想,有弊端也问题不大=》之后看看别人和自己的区别,很棒
可以使用redis中的哈希类型,一个key可以保存多个键值对,方便存储用户信息,其中每个field设置成英文,value设置成要存储的信息。具体可参考《redis开发与运维》的2.3.3章节
好的好的,感谢大佬的思路和指导