SpringBoot 동시 접속 자 제어 실현 방법

일반적으로 시스템 은 같은 계 정의 로그 인 인원 을 제한 합 니 다.여러 사람 이 로그 인 하거나 후자 의 로그 인 을 제한 하거나 전 자 를 차 거나 Spring Security 는 이러한 기능 을 제공 합 니 다.본 고 는 Security 를 사용 하지 않 았 을 때 어떻게 수 동 으로 이 기능 을 실현 하 는 지 설명 합 니 다.
demo 기술 선택
  • SpringBoot
  • JWT
  • Filter
  • Redis + Redisson
  • JWT(token)는 Redis 에 저 장 됩 니 다.JSsionId-Session 과 유사 한 관계 로 사용자 가 로그 인 한 후 매번 Header 에 jwt 를 휴대 하 라 고 요청 합 니 다.
    만약 에 session 을 사용한다 면 본 고의 사 고 를 참고 할 수 있 습 니 다.다만 코드 에 약간의 변경 이 필요 합 니 다.
    두 가지 실현 사고.
    타임 스탬프 비교
    username:jwtToken 과 같은 key-value 는 Reids 에서 Filter 논 리 는 다음 과 같 습 니 다.
    
    public class CompareKickOutFilter extends KickOutFilter {
    
      @Autowired
      private UserService userService;
    
      @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 (token.equals(redisToken)) {
          return true;
    
        } else if (StringUtils.isBlank(redisToken)) {
          bucket.set(token);
    
        } else {
          Long redisTokenUnixTime = JWTUtil.getClaim(redisToken, "createTime").asLong();
          Long tokenUnixTime = JWTUtil.getClaim(token, "createTime").asLong();
    
          // token > redisToken    
          if (tokenUnixTime.compareTo(redisTokenUnixTime) > 0) {
            bucket.set(token);
    
          } else {
            //     token
            userService.logout(token);
            sendJsonResponse(response, 4001, "            ");
            return false;
    
          }
    
        }
    
        return true;
    
      }
    }
    
    
    대열 에서 내차다
    
    public class QueueKickOutFilter extends KickOutFilter {
      /**
       *        /                   
       */
      private boolean kickoutAfter = false;
      /**
       *              1
       */
      private int maxSession = 1;
    
      public void setKickoutAfter(boolean kickoutAfter) {
        this.kickoutAfter = kickoutAfter;
      }
    
      public void setMaxSession(int maxSession) {
        this.maxSession = maxSession;
      }
    
      @Override
      public boolean isAccessAllowed(HttpServletRequest request, HttpServletResponse response) throws Exception {
        String token = request.getHeader("Authorization");
        UserBO currentSession = CurrentUser.get();
        Assert.notNull(currentSession, "currentSession cannot null");
        String username = currentSession.getUsername();
        String userKey = PREFIX + "deque_" + username;
        String lockKey = PREFIX_LOCK + username;
    
        RLock lock = redissonClient.getLock(lockKey);
    
        lock.lock(2, TimeUnit.SECONDS);
    
        try {
          RDeque<String> deque = redissonClient.getDeque(userKey);
    
          //         token,        ;    
          if (!deque.contains(token) && currentSession.isKickout() == false) {
            deque.push(token);
          }
    
          //       sessionId        ,    
          while (deque.size() > maxSession) {
            String kickoutSessionId;
            if (kickoutAfter) { //       
              kickoutSessionId = deque.removeFirst();
            } else { //       
              kickoutSessionId = deque.removeLast();
            }
    
            try {
              RBucket<UserBO> bucket = redissonClient.getBucket(kickoutSessionId);
              UserBO kickoutSession = bucket.get();
    
              if (kickoutSession != null) {
                //      kickout       
                kickoutSession.setKickout(true);
                bucket.set(kickoutSession);
              }
    
            } catch (Exception e) {
            }
    
          }
    
          //       ,    ,          
          if (currentSession.isKickout()) {
            //       
            try {
              //   
              userService.logout(token);
              sendJsonResponse(response, 4001, "            ");
    
            } catch (Exception e) {
            }
    
            return false;
    
          }
    
        } finally {
          if (lock.isHeldByCurrentThread()) {
            lock.unlock();
            LOGGER.info(Thread.currentThread().getName() + " unlock");
    
          } else {
            LOGGER.info(Thread.currentThread().getName() + " already automatically release lock");
          }
        }
    
        return true;
      }
    
    }
    
    
    두 가지 방법 을 비교 하 다
  • 첫 번 째 방법 은 논리 가 간단 하고 거 칠 며 하나의 key-value 만 유지 하고 자 물 쇠 를 사용 할 필요 가 없 으 며 단점 을 말 하지 않 으 면 두 번 째 방법 이 유연 하지 않다.
  • 두 번 째 방법 을 좋아 합 니 다.코드 는 우아 하고 유연 하지만 논리 가 상대 적 으로 번 거 롭 습 니 다.그리고 스 레 드 가 안전하게 대기 열 을 조작 하도록 분포 식 자 물 쇠 를 사용 해 야 합 니 다.현재 우리 프로젝트 에서 사용 하 는 것 은 첫 번 째 방법
  • 이다.
    시범 을 보이다
    다운로드 주소:https://gitee.com/yintianwen7/taven-springboot-learning/tree/master/login-control
  • 프로젝트 를 실행 합 니 다.localhost:8887 demo 에 사용자 정 보 를 저장 하지 않 고 사용자 이름 비밀 번 호 를 임의로 입력 하면 사용자 이름 이 같 으 면 차 입 니 다
  • localhost:8887/index.html 에 사용자 정 보 를 팝 업 하면 현재 사용자 에 게 유효 합 니 다
  • 다른 브 라 우 저 는 같은 사용자 이름 으로 로그 인하 여 첫 번 째 브 라 우 저 로 돌아 가 페이지 를 새로 고침 하고 차 였 음 을 알려 줍 니 다
  • application.properties 에서 어떤 필터 모드 를 여 는 지 선택 하 십시오.기본 값 은 시간 스탬프 를 비교 하여 차 는 것 입 니 다.대기 열 을 열 어 quue-filter.enabled=true
  • 를 차 는 것 입 니 다.
    이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.

    좋은 웹페이지 즐겨찾기