티스토리 뷰

 

- 빠른 성능: 인메모리 기반인 Redis는 빠른 응답 속도를 제공합니다.

- 유연한 데이터 만료 관리: Redis는 각 키에 대해 만료 시간을 설정할 수 있어, Refresh Token의 유효기간 관리가 용이합니다.

- 내구성: Redis는 데이터를 디스크에도 저장할 수 있어 서버 재시작 시에도 데이터를 유지할 수 있습니다.

이러한 이유 때문에 Redis를 이용해서 토큰을 재발급받았습니다. 

 

1️⃣ Access Token과 Refresh Token 발급

사용자가 성공적으로 로그인하면, JWT를 사용하여 사용자의 ID와 기타 정보를 포함한 Access Token과 Refresh Token을 발급합니다. Access Token은 짧은 유효 시간을 가지며, Refresh Token은 상대적으로 긴 유효 시간을 가집니다.

 

2️⃣ Redis에 Refresh Token 저장

발급된 Refresh Token과 해당 토큰이 속한 사용자의 ID를 사용하여 Redis에 데이터를 저장합니다. 이때, key는 사용자의 ID, value는 Refresh Token으로 설정합니다. 또한, 이 데이터의 만료 시간을 Refresh Token의 만료 시간과 동일하게 설정하여 Refresh Token이 만료되면 Redis에서 자동으로 데이터를 제거하게 합니다.

 

3️⃣ Refresh Token 검증

클라이언트가 새로운 Access Token을 요청할 때, 클라이언트로부터 사용자의 ID와 Refresh Token을 받습니다. Redis에서 사용자의 ID를 key로 사용하여 저장된 Refresh Token을 가져옵니다. 만약 저장된 Refresh Token이 존재하고, 받은 Refresh Token과 일치하면 새로운 Access Token을 발급합니다. 만약 일치하지 않거나 저장된 Refresh Token이 없다면, Refresh Token이 유효하지 않다는 메시지를 반환합니다.

 

❗️Redis 설치 및 환경설정은 과거에 정리한 글 참조 ⤵️ (0번부터 1번까지)

 

Redis를 이용해 현재 로그인 중인 사용자 확인하기

0. Redis 설치 [EC2+Docker]✔️redis 이미지 다운docker pull redis ✔️이미지 파일 확인docker images✔️ AWS 보안그룹 인바운드/아웃바인드 설정✔️ docker를 사용하여 redis 이미지를 만들고 redis 비밀번호를

minsu20.tistory.com

 

2. RefreshTokenRepository 작성

Repository를 인터페이스로 정의하지 않고, 직접 아래와 같이 구현한다.

@Repository
public class RefreshTokenRepository {

    private RedisTemplate redisTemplate;

    @Value("${jwt.refresh-token-validity-in-seconds}")
    private long refreshTokenValidityTime;

    public RefreshTokenRepository(final RedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    public void save(String refreshToken, Long userId) {
        redisTemplate.opsForValue().set(String.valueOf(userId), refreshToken, refreshTokenValidityTime, TimeUnit.SECONDS);
    }

    public Optional<String> findById(final Long userId) {
        String refreshToken=(String)redisTemplate.opsForValue().get(String.valueOf(userId));
        return Optional.ofNullable(refreshToken);
    }
}

✔️ save (): refreshToken을 저장하는 함수

     key: userId, value: refreshToken, expire-time:내가 설정한 refreshToken 만료시간, timeUnit: Seconds이용

✔️ findById(): userId를 이용해서 refreshToken을 찾는 함수

⚠️ 의외로 주의할 점 : key는 String 형태여야 한다(RedisConfig에서 내가 그렇게 설정했기 때문) 그래서 String.valueOf()를 이용해서 String으로 바꾸어주어야 합니다!

 

3. createToken 수정

토큰을 만들 때 refreshTokenRepository의 save메소드를 호출해서 redis에 저장을 한다!

 /**
     * 토큰 만드는 함수
     * @param authentication
     * @return TokenInfoResponse
     */
    public TokenInfoResponse createToken(Authentication authentication, boolean isAdditionalInfoProvided, Long userId) {
        String authorities = authentication.getAuthorities().stream()
                .map(GrantedAuthority::getAuthority)
                .collect(Collectors.joining(","));

        long now = (new Date()).getTime();
        Date accessTokenValidity = new Date(now + this.accessTokenValidityTime);
        Date refreshTokenValidity = new Date(now + this.refreshTokenValidityTime);

        String accessToken = Jwts.builder()
                .setSubject(authentication.getName())
                .claim(AUTHORITIES_KEY, authorities)
                .claim(ADDITIONAL_INFO, isAdditionalInfoProvided) // 추가 정보 입력 여부를 클레임에 추가
                .signWith(key, SignatureAlgorithm.HS512)
                .setExpiration(accessTokenValidity)
                .compact();

        String refreshToken = Jwts.builder()
                .setExpiration(refreshTokenValidity)
                .signWith(key, SignatureAlgorithm.HS256)
                .compact();

        refreshTokenRepository.save(refreshToken, userId);

        return TokenInfoResponse.from("Bearer", accessToken, refreshToken, refreshTokenValidityTime);

    }

 

4.  RefreshTokenService 작성

public RefreshTokenDto.RefreshTokenResponse refreshToken(RefreshTokenDto.RefreshTokenRequest refreshTokenRequest) {
        //1. refreshToken 검증
        refreshTokenRepository.findById(refreshTokenRequest.getUserId()).orElseThrow(() -> new NotFoundRefreshToken());
        //2. 새로운 accessToken 재발급
        //2.1 시큐리티 설정
        User user = userRepository.findById(refreshTokenRequest.getUserId()).orElseThrow(() -> new NotFoundEmailException());
        List<GrantedAuthority> authorities = userService.initAuthorities();
        OAuth2User userDetails = userService.createOAuth2UserByUser(authorities, user);
        OAuth2AuthenticationToken auth = userService.configureAuthentication(userDetails, authorities);
        //2.2 JWT 토큰 생성
        TokenInfoResponse tokenInfoResponse = tokenProvider.createToken(auth, true, user.getUserId());
        return RefreshTokenDto.RefreshTokenResponse.from(tokenInfoResponse);
    }

✔️ refreshTokenRepository의 findById를 이용해서 refreshToken을 검증하고, 못 찾으면 에러를 던져줍니다.

✔️ refreshToken이 검증이 되면 시큐리티를 설정하고 Jwt토큰을 생성해서 줍니다.

'Spring > Redis' 카테고리의 다른 글

Redis를 이용해 현재 로그인 중인 사용자 확인하기  (6) 2023.03.18