在web应用中假如没有做会话控制,会出现这样的情况,A登录了账号,B也登录了账号,都是同样的账号,A修改了信息,B会看到修改的信息,这样的用户体验不好,B会觉得我没有修改啊,为什么信息会改变。而做会话控制后,A先登录,B再登录,那么B会把A的登录挤下线。
实现思路:采用时间戳比较
1首先用户登录时,后台是不需要做拦截的,前台把用户名和密码传到后台,后台生成JWT格式的token给前台,并以token为key,用户信息为value存入redis中
2其他url路径,过滤器会拦截请求,先判断前台是否携带了token,或者是携带了token,但是已经失效了(redis中查不到)会直接返回前台错误信息,这时候会以用户名为key,从redis中查询token,没有就以key为用户名,value为token存入redis.放行请求,若从redis查询的token和从前台传进来的token相同,放行请求,若从redis查询的token和从前台传进来的token不相同,如果从前台传递的token的时间戳大于redis中的,则覆盖redis中的token,否认注销token,返回您的账号已经在其他设备登录,拦截请求。
public class CompareKickOutFilter extends KickOutFilter { @Override public boolean isAccessAllowed(HttpServletRequest request, HttpServletResponse response) { String token = request.getHeader("Authorization"); String username = JWTUtil.getUsername(token); String userKey = PREFIX + username; RBucket<String> bucket = redissonClient.getBucket(userKey); String redisToken = bucket.get(); if (StringUtils.isBlank(redisToken)) {// 第一次设置 bucket.set(token); } else if (token.equals(redisToken)) { // 相同的token return true; } else { Long redisTokenUnixTime = JWTUtil.getClaim(redisToken, "createTime").asLong(); Long tokenUnixTime = JWTUtil.getClaim(token, "createTime").asLong(); if (tokenUnixTime.compareTo(redisTokenUnixTime) > 0) { // 传进来的token是离现在最新的,覆盖旧的token bucket.set(token); } else { // 注销当前token userService.logout(token); sendJsonResponse(response, 4001, "您的账号已在其他设备登录"); return false; } } return true; } }
项目结构:
项目地址:https://github.com/jake1263/loginCtl
原文地址:https://www.cnblogs.com/moris5013/p/11128135.html
时间: 2024-11-08 22:10:00