JWT 기반 인증 7편 - Refresh Token 도입 (2)

2023. 9. 8. 14:11Development

안녕하세요!

이번 시간엔 정말 정말 정말로 토큰에 대한 구현을 마무리해보려고 해요.

그리고 테스트까지 진행해서 우리가 원하는 대로 돌아가는지 확인도 해볼게요.

 

오늘 구현은 매우 쉬우니 안심하셔도 돼요.

마음 편하게 읽어주세요.

 

이 게시물은 Refresh Token에 대해 다루고 있고, 설계와 Access Token의 후행 단계예요.

 

설계에 관한 글을 보고 오시지 않은 분들은 이 게시물을 먼저 보는 것을 추천 드릴게요.

 

JWT 기반 인증 2편 - 스프링 시큐리티 JWT 설계

안녕하세요! 이번 시간에는 스프링 시큐리티에서 JWT의 인증 흐름을 다뤄보려고 해요. 스프링 시큐리티에서 JWT를 구현하기 위한 설계 단계라고 봐주셨으면 좋겠어요. 소프트웨어 공학에서는 설

dalmeng-commeng.tistory.com

Access Token의 구현에 관한 글을 보고 오시지 않은 분들은 이 게시물을 먼저 보는 것을 추천드릴게요.

 

JWT 기반 인증 3편 - 스프링 시큐리티 JWT 인증 · 인가 구현 (1)

안녕하세요! 지금부터 본격적인 구현에 들어가 보려고 해요. 저번 시간에 정리한 설계 순서대로 구현을 할 예정이니, 이전 게시물을 보고 오시는 것을 추천드려요. https://dalmeng-commeng.tistory.com/4 J

dalmeng-commeng.tistory.com

이 프로젝트에서 작성한 모든 코드는 달맹의 깃허브에 있어요!

 

 

GitHub - dalmengs/tokenBasedAuthentication: Token Based Authentication in Spring Security

Token Based Authentication in Spring Security. Contribute to dalmengs/tokenBasedAuthentication development by creating an account on GitHub.

github.com


이번 시간에는 저번 시간에 끝내지 못한 구현을 전부 끝내고 테스트까지 진행해볼게요.

 

1. JWT란? - What is JWT?

2. 스프링 시큐리티 JWT 설계

3. 스프링 시큐리티에서 JWT 인증 · 인가 구현

4. Refresh Token 도입

          4 - 1. Refresh Token 개요

          4 - 2. 이전과 달라지는 점    

          4 - 3. 설정 파일 수정 

          4 - 4. JWT Provider 구현 

          4 - 5. JWT Filter 구현

          4 - 6. 서비스 계층 구현 - Service Layer

          4 - 7. 컨트롤러 계층 구현 - Controller Layer

          4 - 8. 테스트 - Tests

5. 리팩토링


4 - 6. 서비스 계층 구현 - Service Layer

Refresh Token 생성 기능만 넣어주면 돼요.

이 기능은 역시 JWT Provider에 정의되어 있어서 주입받아 사용만 해줄게요.

public String createRefreshToken(String username){
    return jwtUtil.createRefreshToken(username);
}

4 - 7. 컨트롤러 계층 구현 - Controller Layer

로그인이 성공하면 사용자에게 토큰을 다시 돌려줬었죠?

하지만 이제는 Refresh Token까지 돌려줘야 해서 Response DTO가 조금 수정되었어요.

 

크게 수정되진 않았고, Refresh Token 필드가 새로 생겼어요.

@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AuthenticationResponseDto {

    private String accessToken;
    private String refreshToken;
    private String username;

}

기존 코드는 Access Token을 요청하고, 받은 토큰을 헤더에 넣어주었죠?

여기서 Refresh Token도 같이 요청하고, 받은 토큰을 헤더가 같이 넣어주면 돼요.

 

거의 수정된 내용이 없어요.

@PostMapping("/login")
public BaseResponse login(@RequestBody AuthenticationRequestDto authenticationRequest, HttpServletResponse response){
    try{
        User user = authenticationService.login(authenticationRequest.getUsername(), authenticationRequest.getPassword());

        String token = authenticationService.createToken(user);
        String refreshToken = authenticationService.createRefreshToken(authenticationRequest.getUsername());

        response.setHeader("access_token", token);
        response.setHeader("refresh_token", refreshToken);

        log.info("Access Token Created - Username : " + user.getUsername());
        return BaseResponse.OK(new AuthenticationResponseDto(token, refreshToken, user.getUsername()));
    }
    catch (BaseException e){
        return BaseResponse.FAILED(e.getResultCode(), e.getMsg());
    }
    catch (Exception e){
        return BaseResponse.FAILED();
    }
}

4 - 8. 테스트 - Tests

벌써 구현이 다 끝나서 바로 테스트를 진행해볼게요.

Access Token에 의한 인증 테스트는 예전에 진행했으므로 이번에는 생략할게요.

 

제가 진행할 테스트 목록은 아래와 같아요.

  1. 로그인에 성공하면 Refresh Token도 같이 돌려주는가?
  2. Access Token과 Refresh Token이 모두 만료되면 요청이 실패하는가?
  3. Access Token이 만료되었어도 Refresh Token이 만료되지 않았으면 요청을 성공적으로 처리할 수 있는가?

1. 로그인에 성공하면 Refresh Token도 같이 돌려주는가?

기존과 똑같이 로그인 요청을 해주었어요.

{
    "username": "dalmeng",
    "password": "********"
}

예상한 대로 Access Token과 Refresh Token이 같이 온 것을 확인할 수 있죠?


[2 ~ 3] 원활한 테스트를 위해 Access Token의 만료 시간은 10초, Refresh Token의 만료시간은 30초로 설정해주었어요.

 

2. Access Token과 Refresh Token이 모두 만료되면 요청이 실패하는가?

똑같이 로그인을 한 후, 아래 그림과 같이 Access Token과 Refresh Token을 모두 헤더에 넣어 요청해주었어요.

아래 그림과 같이 모든 토큰이 만료되어 요청이 실패한 것을 알 수 있죠?

조금 더 자세한 분석을 위해 로그를 볼게요.

  • 12시 41분 55초 - Access Token, Refresh Token 생성
  • 12시 42분 05초 - Access Token 만료
  • 12시 42분 25초 - Refresh Token 만료
  • 12시 42분 47초
    • 자원 접근 요청
    • 유효하지 않은 토큰 (Invalid Token)
    • 요청 거부

원하는 대로 두 토큰이 모두 만료되었을 때는 자원 접근 요청을 성공적으로 거부했어요.


3. Access Token이 만료되었어도 Refresh Token이 만료되지 않았으면 요청을 성공적으로 처리할 수 있는가?

똑같은 형식으로 로그인하고, 재요청 시간만 달리 했어요.

Access Token은 만료되었지만 Refresh Token이 아직 살아있을 때 요청했어요.

이번에는 예상대로 요청이 성공한 것을 알 수 있죠?

 

예상대로라면 Refresh Token을 확인하고 Access Token을 재발급했어야 돼요.

원하는 대로 흘러갔는지 로그를 확인해볼게요. 

  • 12시 47분 47초 - Access Token, Refresh Token 생성
  • 12시 47분 57초 - Access Token 만료
  • 12시 48분 06초
    • 자원 접근 요청
    • Access Token 만료 확인
    • 유효한 Refresh Token
    • Access Token 재생성
    • 요청 승인
  • 12시 48분 17초 - Refresh Token 만료

모든 테스트를 성공적으로 마무리 했고, Refresh Token을 도입하는데 성공했어요.

Access Token의 유효 시간이 길면 Refresh Token을 도입한 이유가 퇴색된다는 것을 꼭 이해하고, 적절한 유효 시간을 설정해야 해요.

 

지금까지 정말 고생많으셨고, 다음 시간에는 리팩토링 시간으로 돌아올게요.


제 글이 많은 도움이 되었길 바라며,
긴 글 봐주셔서 감사합니다!
멋진 개발자가 되기 위해 더 열심히 달리겠습니다!
- 달맹 -