목표 기능
- 회원가입 (깃허브 참고)
- 로그인
- Access Token, Refresh Token 발급
- Authorization Header 에 Access Token, Refresh Token 담아서 응답 보내기
- 서버에서 Access Token과 Refresh Token을 어디에 담아서 클라이언트에 응답을 줘야할지 고민했다.
Redis 부분은 추후에 포스팅할 예정이다.
개발 환경
spring boot 3.0.2
spring security : 6.0.1
Gradle
Redis
로그인
시퀀스 다이어그램
UserController
- Access Token과 Refresh Token 발급
- jwtTokenProvider.generateToken(user, jwtProperties.getAccessTokenExpiration())
- JWT에 저장할 id, email을 위해 User, 토큰 만료 시간을 파라미터로 넘겨준다.
- Response의 Body에 Access Token, Refresh Token 담아서 응답 보내기
@PostMapping(value = "/login", consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<ResToken> signIn(@RequestBody @Valid ReqUserSignIn req){
//이메일과 비밀번호로 user 조회
User user = userService.signIn(req);
//access token 발급
String accessToken = jwtTokenProvider.generateToken(user, jwtProperties.getAccessTokenExpiration());
//refresh token 발급
String refreshToken = jwtTokenProvider.generateToken(user, jwtProperties.getRefreshTokenExpiration());
//redis에 RT:test@gmail.com(key) / 23jijiofj2io3hi32hiongiodsninioda(value) 형태로 리프레시 토큰 저장하기
//REFRESH_TOKEN_DURATION 초 후에 만료된다(상대적인 시간)
redisTemplate.opsForValue().set(refreshToken, String.valueOf(user.getId()), (int) jwtProperties.getAccessTokenExpiration().toSeconds(), TimeUnit.SECONDS);
ResToken resToken = new ResToken(accessToken,refreshToken,"Bearer");
// JWT 토큰을 응답 바디에 넣어서 클라이언트에게 전달
return ResponseEntity.ok()
.body(resToken);
}
@Getter
@AllArgsConstructor
public class ResToken {
private String accessToken;
private String refreshToken;
private String tokenType;
}
JwtTokenProvider
public String generateToken(User user, Duration expiredAt){
Date now = new Date();
return makeToken(new Date(now.getTime() + expiredAt.toMillis()),user);
}
private String makeToken(Date expiry, User user){
Date now = new Date();
return Jwts.builder()
.setHeaderParam(Header.TYPE, Header.JWT_TYPE)
.setIssuer(jwtProperties.getIssuer())
.setIssuedAt(now)
.setExpiration(expiry)
.setSubject(user.getEmail())
.claim("id",user.getId())
.signWith(SignatureAlgorithm.HS256, jwtProperties.getSecretKey())
.compact();
}
결과
POST /login
Headers
- Authorization :
Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0ZXN0QGdtYWlsLmNvbSIsImlhdCI6MTcwOTAxNjEwOSwiZXhwIjoxNzA5NjIwOTA5LCJzdWIiOiJ0ZXN0QGdtYWlsLmNvbSIsImlkIjoxfQ.65brArU47bq6heR7GdGP8xy_YB9gfeRvXkXMt72uIpw
- Refresh-Token :
Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0ZXN0QGdtYWlsLmNvbSIsImlhdCI6MTcwOTAxNjEwOSwiZXhwIjoxNzExNjA4MTA5LCJzdWIiOiJ0ZXN0QGdtYWlsLmNvbSIsImlkIjoxfQ.jtd2WqdTFzG59Rk_zXAL8dxOwUpt_woZm4CSZdDIDgU
→ 헤더와 페이로드는 같지만 시그니처가 다르다.
다음 포스팅
- AccessToken이 만료된 경우, Refresh Token을 통한 재발급 요청
[JWT] JWT 인증/인가 적용기(4) - AccessToken이 만료된 경우
'Backend > JWT' 카테고리의 다른 글
[JWT] JWT 인증/인가 적용기(4) - AccessToken이 만료된 경우 (0) | 2024.02.28 |
---|---|
[JWT] JWT는 어디에 담아 클라이언트와 요청/응답할까 (0) | 2024.02.27 |
[JWT] JWT 인증/인가 적용기(2) - Spring Security를 통한 JWT 적용 (0) | 2024.01.29 |
[JWT] JWT 인증/인가 구현기(1) - Spring Security 인증 구조 (0) | 2024.01.27 |
[JWT] Access Token과 Refresh Token이란? (0) | 2024.01.27 |