分布式系统:⼀种判断⽤户登录状态的解决⽅案(重复登录⽤户退出关闭⽹页窗⼝关闭浏览器⽹络错误)
这个问题对我这个菜鸡来说有点棘⼿。
先说下需求:
1.禁⽌重复登录
⼩王使⽤admin账号登录系统以后,⼩明再使⽤admin账号登录系统,系统拒绝⼩明的登录并提⽰:此账号已在别的设备登录!
(此处的重复登录以浏览器的会话为根据,也就是说,⼩王使⽤admin账号在chrome上登录系统,稍候⼩王新开⼀个标签页,再次使⽤admin登录系统,此时系统允许登录;但是如果⼩王使⽤admin账号在IE登录系统,此时遭到系统的拒绝。)
判断的依据的session的id。
2.判断⽤户在线/离线
2.1 ⽤户登录系统,默认⼀直保持在线状态;
2.2 ⽤户关闭当前标签页,此时,如果仍然有系统的其他⼦页⾯被打开,则在线;如果系统所有的页⾯都被关闭,则离线;
2.3 ⽤户关闭浏览器,离线;
2.4 ⽤户⽹络错误请求超时,离线。
2.5 ⽤户点击退出系统,离线。
如果不是分布式系统,这个问题还是⾮常好处理的。
session有⼀个getLastAccessedTime()的⽅法,⽤以获取最后⼀次发送ajax请求的时间。
只要在前端页⾯不停地发ajax请求,LastAccessedTime()就会⼀直更新。
只需要⽤当前时间-LastAccessedTime()>允许超时的时间,就可以判断在线离线了。
发送ajax请求的步骤具体操作步骤:
1.创建⼀个静态map,登录账号作为key,当前session作为value;
2.每次登录鉴权的时候,判断当前登录的账号在不在map的key⾥⾯,在的话判断sessionID是否⼀致,⼀致允许登录;不⼀致拒绝登录;
3.每次登录鉴权成功,就把当前的账号和session存进map⾥;
4.创建⼀个定时器:获取当前时间,遍历map的session,获取LastAccessedTime(),判断两者之间的时间差,当时间差⼤于某个值时,判断⽤户离线,使⽤seesion.invalidate()⽅法销毁这个session,以及从map⾥移除session;
5.上⾯的步骤可以解决关闭标签页/关闭浏览器/⽹络错误,⽤户点击退出系统的话,直接写在退出系统的那个service⾥⾯吧。
有个缺陷,就是不能做到即时判断,这真的没办法了。
以及还有个值得注意的问题,就是写完以后做⼀下弱⽹测试,到⼀个合适的时差,不然容易被踢下线。
分布式系统处理这个问题的解决⽅案
前端页⾯依旧不停地发送ajax请求确保session存活。
A:把session往redis上⾯存,⾸先实现session共享,session过期的时候,把它从redis⾥⾯删掉,这⾥存session是存的sessionid的字符串。
记得去l⾥⾯配置session的过期时间。
<session-config>
<session-timeout>1</session-timeout>
</session-config>
单位是分钟。
B:每次登录成功以后,把账号和seesionid作为key-value存进redis中。
每次登录鉴权的时候,判断redis⾥⾯有没有这个登录账号,有的话,判断是不是同⼀个sessiionid,是就允许登录,不是就拒绝登录。
创建⼀个定时器,定时检测A中seesionid还在不在,不在的话,说明这个session已经过期了,就去B中把对应的key-value也删除掉。
差不读就是这样吧,凑合凑合能⽤,记得做下弱⽹测试。