web应用中实现同一个账号,后面登录的会把前面登录的挤下线

  在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

猜你喜欢

转载自www.cnblogs.com/moris5013/p/11128135.html