SpringBoot并发登录人数控制的实现方法

  

通常系统都会限制同一个账号的登录人数,多人登录要么限制后者登录,要么踢出前者,Spring Security提供了这样的功能,本文讲解一下在没有使用安全的时候如何手动实现这个功能

  

<强>演示技术选型

  
      <李> SpringBoot李   <李> JWT李   <李>滤镜李   <李>复述+ Redisson李   
  

智威汤逊(令牌)存储在复述中,类似JSessionId-Session的关系,用户登录后每次请求在标题中携带JWT
  如果你是使用会话的话,也完全可以借鉴本文的思路,只是代码上需要加些改动
  

  

<强>
  

  

<强>比较时间戳
  

  

维护一个用户名:jwtToken这样的一个键值在里德中,过滤器逻辑如下

  

 SpringBoot并发登录人数控制的实现方法

        公开课CompareKickOutFilter延伸KickOutFilter {      @ autowired   私人UserService UserService;      @Override   公共布尔isAccessAllowed (HttpServletRequest请求,HttpServletResponse响应){   字符串标记=request.getHeader(“授权”);   字符串的用户名=JWTUtil.getUsername(令牌);   字符串userKey=前缀+同名的用户名;      RBucket桶=redissonClient.getBucket (userKey)同名;   字符串redisToken=bucket.get ();      如果(token.equals (redisToken)) {   返回true;      }else if (StringUtils.isBlank (redisToken)) {   bucket.set(令牌);      其他}{   长redisTokenUnixTime=JWTUtil。getClaim (redisToken createTime) .asLong ();   长tokenUnixTime=JWTUtil。getClaim(令牌,“createTime”) .asLong ();//令牌比;redisToken则覆盖   如果(tokenUnixTime.compareTo (redisTokenUnixTime)比;0){   bucket.set(令牌);      其他}{//注销当前令牌   userService.logout(令牌);   sendJsonResponse(响应,4001,“您的账号已在其他设备登录”);   返回错误;      }      }      返回true;      }   }      之前      

<强>队列踢出

  

 SpringBoot并发登录人数控制的实现方法

        公开课QueueKickOutFilter延伸KickOutFilter {/* *   *踢出之前登录的/之后登录的用户默认踢出之前登录的用户   */私人布尔kickoutAfter=false;/* *   *同一个帐号最大会话数默认1   */私人int maxSession=1;      公共空间setKickoutAfter(布尔kickoutAfter) {   这一点。kickoutAfter=kickoutAfter;   }      公共空间setMaxSession (int maxSession) {   这一点。maxSession=maxSession;   }      @Override   公共布尔isAccessAllowed (HttpServletRequest请求,HttpServletResponse响应){抛出异常   字符串标记=request.getHeader(“授权”);   UserBO currentSession=CurrentUser.get ();   断言。notNull (currentSession“currentSession不能空”);   字符串的用户名=currentSession.getUsername ();   字符串userKey=前缀+同名“deque_”+用户名;   字符串lockKey=PREFIX_LOCK +用户名;      RLock锁=redissonClient.getLock (lockKey);      锁。锁(2,TimeUnit.SECONDS);      尝试{   RDeque双端队列=redissonClient.getDeque (userKey)同名;//如果队列里没有此令牌,且用户没有被踢出,放入队列   如果(! deque.contains(令牌),,currentSession.isKickout ()==false) {   deque.push(令牌);   }//如果队列里的sessionId数超出最大会话数,开始踢人   而(deque.size()在maxSession) {   字符串kickoutSessionId;   如果(kickoutAfter){//如果踢出后者   kickoutSessionId=deque.removeFirst ();   其他}{//否则踢出前者   kickoutSessionId=deque.removeLast ();   }      尝试{   RBucket桶=redissonClient.getBucket (kickoutSessionId);   UserBO kickoutSession=bucket.get ();      如果(kickoutSession !=null) {//设置会话的解雇属性表示踢出了   kickoutSession.setKickout(真正的);   bucket.set (kickoutSession);   }      }捕捉(异常e) {   }      }//如果被踢出了,直接退出,重定向到踢出后的地址   如果(currentSession.isKickout ()) {//会话被踢出了   尝试{//注销   userService.logout(令牌);   sendJsonResponse(响应,4001,“您的账号已在其他设备登录”);      }捕捉(异常e) {   }      返回错误;      }      最后}{   如果(lock.isHeldByCurrentThread ()) {   lock.unlock ();   LOGGER.info (Thread.currentThread () . getname() +“解锁”);      其他}{   LOGGER.info (Thread.currentThread () . getname() +“已经自动释放锁”);   }   }      返回true;   }      }      

SpringBoot并发登录人数控制的实现方法