슬기로운슬기

1. AccessToken과 RefreshToken을 사용한 인증 단계

1.1. 인증(로그인) 단계

  • 사용자가 아이디와 비밀번호로 로그인을 하면, 서버는 ATK와 RTK를 발급함
  • ATK : 사용자의 세션을 식별하는데 사용
  • RTK : ATK가 만료되었을 때 새로운 ATK를 발급받을 수 있게 해줌 

1.2. AccessToekn 만료 시

  • ATK가 만료되면, 서버에서는 클라이언트에게 에러를 반환하거나, 클라이언트에서 새로운 ATK를 요청하도록 유도해야함
  • 클라이언트는 RTK와 함께 새로운 ATK를 요청함

1.3. RefreshToken을 사용한 Access Token 재발급

  • 클라이언트는 RTK과 함께 서버에 ATK 재발급을 요청
  • 서버는 클라이언트의 요청을 검증, 유효한 경우 새로운 ATK를 발급해줌 

이러한 순서로 진행될 예정이다. RefreshToken 검증을 위해 Redis를 사용하였다. 

 

2. UserController (로그인, 엑세스 토큰 재발급 API)

<bash />
@Operation(summary = "로그인") @PostMapping("/login") public ResponseEntity<TokenResponseDto> login(@RequestBody LoginRequestDto requestDto) { TokenResponseDto result = userService.login(requestDto); return ResponseEntity.status(HttpStatus.OK).body(result); } @Operation(summary = "엑세스 토큰 재발급") @GetMapping("/reissue") public TokenResponseDto reissue( @AuthenticationPrincipal UserDetailsImpl userDetails, HttpServletRequest request) { TokenResponseDto result = userService.reissue(userDetails.getUser(), request); return result; }

 

3. TokenResponseDto

<bash />
@Getter @AllArgsConstructor public class TokenResponseDto { private final String atk; private final String rtk; }
  • ResponseBody에 AccessToken과 RefreshToken을 넣어줄 것 이다.
  • 프론트랑 회의를 하고 이렇게 진행하게 되었다..  

4. UserService

4.1. [로그인 메서드]

<bash />
@Override public TokenResponseDto login(LoginRequestDto dto) { String username = dto.getUsername(); String password = dto.getPassword(); // User 찾기 User user = findUser(username); // password 확인 passwordCheck(password, user.getPassword()); // JWT 생성 String atk = jwtUtil.createToken(username, user.getRole()); String rtk = jwtUtil.createRefreshToken(username, user.getRole()); // RefreshToken Redis 저장 redisUtil.saveRefreshToken(user.getUsername(), rtk); return new TokenResponseDto(atk, rtk); }
  • 사용자의 로그인을 처리하는 메서드 
  • 로그인 시 엑세스토큰, 리프레시토큰 생성  
  • 리프레시토큰은 Redis에 저장 -> 나중에 엑세스 토큰 재발급 할 때 필요 ! (key: username, value: RefreshToken)

4.2. [엑세스 토큰 재발급 메서드]

<bash />
@Override public TokenResponseDto reissue(User user, HttpServletRequest request) { String newAtk = jwtUtil.createToken(user.getUsername(), user.getRole()); return new TokenResponseDto(newAtk, null); }
  • 액세스 토큰을 재발급하는 메서드
  • 새로운 엑세스 토큰 생성
  • Request : HttpServletRequest로 RefreshToken을 받을 예정
  • Response : RefreshToken은 사용하지 않기 때문에 null로 설정 
  • RefreshToken에 대한 확인은 Jwt 검증 필터에서 할 예정 

 

5. JwtUtil

5.1. [AccessToken 생성 메서드]

<bash />
// access token 토큰 생성 public String createToken(String username, UserRoleEnum role) { Date date = new Date(); return BEARER_PREFIX + Jwts.builder() .setSubject(username) // 사용자 식별자값(ID) .claim(AUTHORIZATION_KEY, role) //사용자 권한 .setExpiration(new Date(date.getTime() + TOKEN_TIME)) // 만료 시간 .setIssuedAt(date) // 발급일 .signWith(key, signatureAlgorithm) // 암호화 알고리즘 .compact(); }
  • 만료시간은 1시간으로 설정 

 

5.2. [RefreshToken 생성 메서드]

<bash />
// refresh token 생성 public String createRefreshToken(String username, UserRoleEnum role) { Date now = new Date(); return BEARER_PREFIX + Jwts.builder() .setSubject(username) // 사용자 식별자값(ID) .claim(AUTHORIZATION_KEY, role) //사용자 권한 .setExpiration(new Date(now.getTime() + REFRESH_TOKEN_TIME)) //만료시간 .signWith(key, signatureAlgorithm) .compact(); }
  • 만료시간 1주일 설정

5.3. [RefreshToken 확인 메서드]

<bash />
// refreshToken 확인 public boolean checkRefreshToken(String username, String token) { String storedToken = redisUtil.getRefreshToken(username); return storedToken !=null && storedToken.substring(7).equals(token); }
  • Redis에 저장한 RefreshToken과 Header의 request와 같은지 확인

 

6. JwtAuthorizationFilter-Jwt 검증 필터

<bash />
@Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String token = jwtUtil.resolveToken(request); if(token != null) { if(!jwtUtil.validateToken(token)){ log.error("Token Error"); return; } if(request.getRequestURI().equals("/home/users/reissue")) { String username = jwtUtil.getUsernameFromToken(token); if(!jwtUtil.checkRefreshToken(username, token)) { log.error("Refresh token check failed"); return; } } Claims info = jwtUtil.getUserInfoFromToken(token); setAuthentication(info.getSubject()); } try { filterChain.doFilter(request, response); } catch (FileUploadException e) { log.error(e.getMessage()); } }
  • 여기서 추가적인 처리를 해준 부분은 request.getRequestURI().equals("/home/users/reissue") 이다.
  • 요청 URI가 "/home/users/reissue"인 경우에 리프레시 토큰의 유효성을 검사하는 메서드를 실행한다. 
반응형
profile

슬기로운슬기

@스를기

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!