본문 바로가기
DB/Redis

TIL 230904 : Redis 2-1 - RefreshToken을 이용해 자동으로 AccessToken 재발급하기 (Spring Security)

by 우인입니다 2023. 9. 8.

 

https://thiswooin.tistory.com/94

 

 

TIL 230901 : Redis 2 - RefreshToken구현하기. 발급 및 Redis저장. (Spring Security)

https://thiswooin.tistory.com/92 TIL 230830 : Redis 1 - Spring에서 연동하기 (lettuce, jedis, RedisTemplate, CRUDRepository 활용) 지난 시간에 로컬로 Redis서버를 실행하고 RedisInsight라는 GUI를 이용해 Redis 데이터를 저장해

thiswooin.tistory.com

 

RefreshToken을 발급하고 클라이언트로 보내는 과정까지 했다.

이번엔 RefreshToken의 존재이유인 AccessToken만료시 RefreshToken을 통해 Token값을 재발급받는 코드를 작성해봤다.

 


0. Refactor

토큰을 쿠키에 담고, 쿠키에서 가져오도록 변경

지난 번 코드 작성 이후에 프론트와의 코드를 리뷰하다 토큰을 서버-클라이언트 주고받을 때 쿠키에 담아서(Set-Cookie) 전달하는 식으로 변경됐다.

 

JwtAuthorizationFilter

@Override
    protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain filterChain) throws ServletException, IOException {

        String[] tokens = jwtUtil.getTokensFromRequest(req);

        String accessToken = tokens[0];
        String refreshToken = tokens[1];
        
        ... //토큰 검증하는 코드 계속

 

JwtUtil.getTokensFromRequest

    //쿠키에서 JWT가져오기 : HttpServletRequest 에서 Cookie Value
    public String[] getTokensFromRequest(HttpServletRequest req) throws UnsupportedEncodingException {
        Cookie[] cookies = req.getCookies();
        String[] tokens = new String[2]; // 0 : AccessToken, 1 : RefreshToken
        if (cookies != null) {
            for (Cookie cookie : cookies) {
                if (cookie.getName().equals(AUTHORIZATION_HEADER)) {
                    tokens[0] = URLDecoder.decode(cookie.getValue(), "UTF-8"); // Encode 되어 넘어간 Value 다시 Decode
                }
                if (cookie.getName().equals(REFRESH_TOKEN_HEADER)) {
                    tokens[1] = URLDecoder.decode(cookie.getValue(), "UTF-8"); // Encode 되어 넘어간 Value 다시 Decode
                }
            }
        }
        return tokens;
    }

 

고민되는 부분

String[ ] tokens : AccessToken, RefreshToken 두 값만 가져오는 게 명확해서 배열로 설정하고 인덱스 번호에 맞게 가져오게 되어있는데 이 부분이 가독성이 떨어지진 않을까. Hash를 쓰면 key값에 어떤 데이터가 있는 지 가독성이 좋아질텐데, 성능상에 이슈는 없을 지. 해시가 읽기가 빠르니 별 문제는 없지 않을까 싶지만 배열도 읽기는 빠르다.

 

 

 


1. RefreshToken을 이용해 AccessToken 재발급 받기

WebSecurityConfig에서 인증인가가 필요한 URI에 접근하면 이에 다른 필터를 통과해야한다.

즉, JwtAuthorization에서 처음 AccessToken을 이용해 검증을 받고자 했으나 짧은 만료시간으로 인해 검증이 실패했을 경우 RefreshToken을 통해 재발급 받는 코드를 추가하는 식으로 코드를 짰다.

 

   @Override
    protected void doFilterInternal(...) {

		...//토큰값 가져오는 코드

        if (StringUtils.hasText(accessToken)) {
        //토큰이 있긴 할 때
            if (!jwtUtil.validateToken(accessToken)) {
                if (jwtUtil.validateToken(refreshToken)) {
                    //AccessToken은 만료, RefreshToken은 살아있을 때 
                    //AccessToken과 RefreshToken 재발급 후 해당 Jwt로 인증인가 진행

                    log.info("accessToken 재발급");
					
                    //쿠키에서 RefreshToken을 가져오기.
                    Claims infoFromRefreshToken = jwtUtil.getUserInfoFromToken(refreshToken);
					
                    //Redis에서 RefreshToken을 가져오기.
                    String username = infoFromRefreshToken.getSubject();
                    UserToken foundTokenDto = findFoundTokenDtoFromRedis(username);

					//Cookie, Redis에서 각각 가져온 RefreshToken이 맞는 지 확인 후 재발급
                    if (foundTokenDto.getRefreshToken().equals(refreshToken)) {
                        //재발급
                        User user = findUserByUsername(username);
                        tokens = jwtUtil.addJwtToCookie(user, res);
                    }
                } else {
                	// 모든토큰 만료시 코드
                }
            }
		// 재발급 받은 코드로 인증정보 가져오는 코드
        // 결과적으로 해당 코드가 실행 됨 : SecurityContextHolder.setContext(context);
    }

 

 

 

2.Postman 테스트

우선 테스트를 위해 AccessToken의 만료시간을 5초로 설정해두었다.

    private final int ACCESS_TOKEN_TIME_SECONDS = 5;
    private final long ACCESS_TOKEN_TIME = ACCESS_TOKEN_TIME_SECONDS * 1000L;

 

1) 최초 로그인

 : username 'wooin0821'로 로그인을 성공했고 해당 토큰값을 아래처럼 받았다.

 

RedisDB에도 아래처럼 토큰값이 잘 들어와있다.

 

 

2) 만료이후 인가필요한 api 요청

 : 현재 5초가 지나서 AccessToken은 만료가 됐을 것이다. Postman을 통해 review를 post보내보는 요청을 날려본다.

 

Postman에도 해당 쿠키가 잘 담겨있다.

 

쿠키에 토큰 값 확인

 

redis에서 값 대조해보며 확인

 

새로운 토큰 발행

 

Redis에도 반영

 

새로운 토큰으로 인증정보 생성

 

클라이언트에도 새로운 쿠키 적용 완료

 

등록 완료

7번 연관관계를 갖는 외래키와 함께 DB에 잘 생성되었다.

 


더 공부할 것

 

Security상에서 User의 정보를 가져오는 과정, 쿠키값을 가져오고 기존 쿠키값을 설정하는 방법 등이 아직 개운치 못 하다.