分布式系统:一种判断用户登录状态的解决方案(重复登录/用户退出/关闭网页窗口/关闭浏览器/网络错误)

这个问题对我这个菜鸡来说有点棘手。

先说下需求:

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请求,session.getLastAccessedTime()就会一直更新。
只需要用当前时间-session.getLastAccessedTime()>允许超时的时间,就可以判断在线离线了。

具体操作步骤:

1.创建一个静态map,登录账号作为key,当前session作为value;
2.每次登录鉴权的时候,判断当前登录的账号在不在map的key里面,在的话判断sessionID是否一致,一致允许登录;不一致拒绝登录;
3.每次登录鉴权成功,就把当前的账号和session存进map里;
4.创建一个定时器:获取当前时间,遍历map的session,获取session.getLastAccessedTime(),判断两者之间的时间差,当时间差大于某个值时,判断用户离线,使用seesion.invalidate()方法销毁这个session,以及从map里移除session;
5.上面的步骤可以解决关闭标签页/关闭浏览器/网络错误,用户点击退出系统的话,直接写在退出系统的那个service里面吧。

有个缺陷,就是不能做到即时判断,这真的没办法了。

以及还有个值得注意的问题,就是写完以后做一下弱网测试,找到一个合适的时差,不然容易被踢下线。

分布式系统处理这个问题的解决方案

前端页面依旧不停地发送ajax请求确保session存活。

A:把session往redis上面存,首先实现session共享,session过期的时候,把它从redis里面删掉,这里存session是存的sessionid的字符串。

记得去web.xml里面配置session的过期时间。

	<session-config>
		<session-timeout>1</session-timeout>
	</session-config>

单位是分钟。

B:每次登录成功以后,把账号和seesionid作为key-value存进redis中。

每次登录鉴权的时候,判断redis里面有没有这个登录账号,有的话,判断是不是同一个sessiionid,是就允许登录,不是就拒绝登录。

创建一个定时器,定时检测A中seesionid还在不在,不在的话,说明这个session已经过期了,就去B中把对应的key-value也删除掉。

差不读就是这样吧,凑合凑合能用,记得做下弱网测试。

posted on 2021-07-06 15:52  northwest  阅读(805)  评论(0编辑  收藏  举报

导航