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"인 경우에 리프레시 토큰의 유효성을 검사하는 메서드를 실행한다.
반응형
'study > study_spring' 카테고리의 다른 글
[TIL] TDL프로젝트 - 유저 검색 기능 구현 (Spring) (0) | 2023.07.18 |
---|---|
[S.A] TDL 프로젝트 (0) | 2023.07.17 |
[TIL] 좋아요 기능 구현 (Java) (0) | 2023.07.12 |
[TIL] 게시물 조회 메서드 (+댓글) (0) | 2023.07.11 |
[TIL] 회원가입 구현 (0) | 2023.06.26 |