2025-05-20
AccessToken์ ๋ณดํต ์งง์ ์๋ช ์ ๊ฐ์ง๋ฉฐ, ํด๋ผ์ด์ธํธ๋ ๋ง๋ฃ ์ RefreshToken์ ํตํด ๋ค์ ์ธ์ฆํด์ผ ํ๋ค.
๊ทธ๋ ๊ธฐ ๋๋ฌธ์ RefreshToken์ ์๋ฒ์ ์ ์ฅํด๋๋ ๊ฒ์ด ์ค์ํ๋ค.
โ ์๋ฒ(DB)์ RefreshToken์ ์ ์ฅํ๋ฉด:
- ๐ก๏ธ ํ์ทจ ์ฌ๋ถ ํ์ธ ๊ฐ๋ฅ: ๊ณต๊ฒฉ์๊ฐ ์์๋ก ๋ง๋ RefreshToken์ ์๋ฒ์์ ํ์ธ ๊ฐ๋ฅ
- ๐ ๋ถํ์ํ ์ฌ๋ก๊ทธ์ธ ๋ฐฉ์ง: AccessToken๋ง ๋ง๋ฃ๋ ๊ฒฝ์ฐ, ๋ค์ ๋ก๊ทธ์ธํ ํ์ ์์ด ์๋ก ๋ฐ๊ธ ๊ฐ๋ฅ
- ๐ ๋ก๊ทธ์์ ์ ๊ฐ์ ๋ง๋ฃ ๊ฐ๋ฅ: ์๋ฒ์์ ํด๋น ์ฌ์ฉ์์ ํ ํฐ์ ์ ๊ฑฐํ๋ฉด ์๋ ๋ก๊ทธ์์ ํจ๊ณผ
๐ ์ด ํ๋ฆ์ ์ํด
JwtToken
ํ ์ด๋ธ์ AccessToken + RefreshToken์ ํจ๊ป ์ ์ฅํ๋ค.
๋จ๊ณ | ์ค๋ช |
---|---|
๋ก๊ทธ์ธ ์ฑ๊ณต | AccessToken + RefreshToken ์์ฑ |
AccessToken โ ํด๋ผ์ด์ธํธ | ์ฟ ํค์ ์ ์ฅ (HttpOnly , Secure ์ ์ฉ ๊ฐ๋ฅ) |
RefreshToken โ ์๋ฒ | DB(JwtToken Entity)์ ์ ์ฅ |
์ธ์ฆ ์์ฒญ | ํด๋ผ์ด์ธํธ๊ฐ AccessToken์ ํค๋๋ก ์ ์ก |
AccessToken ๋ง๋ฃ | ํด๋ผ์ด์ธํธ๊ฐ RefreshToken๊ณผ ํจ๊ป /refresh ์์ฒญ |
RefreshToken ๊ฒ์ฆ ์ฑ๊ณต | ์๋ก์ด AccessToken ๋ฐ๊ธ ๋ฐ ์ฟ ํค ๊ฐฑ์ |
RefreshToken ๋ง๋ฃ or ์์ | ์ ์ฒด ์ฌ๋ก๊ทธ์ธ ์๊ตฌ |
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Entity
public class JwtToken {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name="accessToken", columnDefinition = "TEXT", nullable = false)
private String accessToken;
@Column(name="refreshToken", columnDefinition = "TEXT", nullable = false)
private String refreshToken;
@Column(name="username", nullable = false)
private String username;
@Column(name="createAt", nullable = false)
private LocalDateTime createAt;
}
@Slf4j
@Component
public class CustomSuccessHandler implements AuthenticationSuccessHandler {
@Autowired
private JwtTokenProvider jwtTokenProvider;
@Autowired
private JwtTokenRepository jwtTokenRepository;
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
TokenInfo tokenInfo = jwtTokenProvider.generateToken(authentication);
// AccessToken โ ์ฟ ํค๋ก ํด๋ผ์ด์ธํธ ์ ๋ฌ
Cookie cookie = new Cookie(JwtProperties.ACCESS_TOKEN_COOKIE_NAME, tokenInfo.getAccessToken());
cookie.setMaxAge(JwtProperties.REFRESH_TOKEN_EXPIRATION_TIME);
cookie.setPath("/");
response.addCookie(cookie);
// AccessToken + RefreshToken โ DB ์ ์ฅ
JwtToken jwtToken = JwtToken.builder()
.accessToken(tokenInfo.getAccessToken())
.refreshToken(tokenInfo.getRefreshToken())
.username(authentication.getName())
.createAt(LocalDateTime.now())
.build();
jwtTokenRepository.save(jwtToken);
response.sendRedirect(request.getContextPath() + "/");
}
}
/refresh
์์ฒญ์ ๋ณด๋ (RefreshToken ํฌํจ)์ํฉ | ์ค๋ช | ์ฒ๋ฆฌ ์ ๋ต (์์ ) |
---|---|---|
โ ์ต์ด ๋ก๊ทธ์ธ(Client: AT โ / DB: โ) | ์๋ก ๋ฐ๊ธํด์ ํด๋ผ์ด์ธํธ ์ ๋ฌ + DB ์ ์ฅ | ์ ์ ํ๋ฆ |
โ ๊ธฐ์กด ๋ก๊ทธ์ธ(Client: AT โ / DB: โ ) | ํ ํฐ ์ ํจ ์ ๋ฌธ์ ์์ | ์ ์ ํ๋ฆ |
๐ AccessToken ๋ง๋ฃ / RefreshToken ์กด์ฌ | /refresh ์์ฒญ ํตํด ์ฌ๋ฐ๊ธ | ์๋ก์ด AT ๋ฐ๊ธ |
โ ๏ธ AccessToken ๋ง๋ฃ / RefreshToken ์์ | ์ฌ๋ก๊ทธ์ธ ํ์ | ๋ก๊ทธ์ธ ํ์ด์ง๋ก ๋ฆฌ๋ค์ด๋ ํธ |
โ Client: AT โ / DB: โ | ์ฟ ํค ์ ๊ฑฐ๋๊ฑฐ๋ ๋ง๋ฃ๋ ๊ฒฝ์ฐ | Refresh๋ก ์ธ์ฆ ๊ฐ๋ฅ / ์์ผ๋ฉด ๋ก๊ทธ์ธ ์ ๋ |
๐งจ RefreshToken ์กฐ์๋จ or ํ์ทจ๋จ | ๋ณด์ ์ํ | ์๋ฒ์์ ์ฐจ๋จ ํ ์ ์ฒด ์ธ์ ๋ก๊ทธ์์ ํ์ (๋ธ๋๋ฆฌ์คํธ ๋ฑ ํ์ฉ ์์ ) |
๊ตฌ์ฑ์์ | ์ค๋ช |
---|---|
JwtToken Entity | Access/RefreshToken๊ณผ ์ฌ์ฉ์ ์ ๋ณด๋ฅผ ํจ๊ป ์ ์ฅ |
CustomSuccessHandler | ๋ก๊ทธ์ธ ์ฑ๊ณต ์ ํ ํฐ ๋ฐ๊ธ ๋ฐ ์ ์ฅ ์ฒ๋ฆฌ |
JwtTokenRepository | DB์์ ํ ํฐ ์กฐํ ๋ฐ ๊ด๋ฆฌ |
/refresh (์ถํ ๊ตฌํ) | AccessToken ์ฌ๋ฐ๊ธ ์ฒ๋ฆฌ ์๋ํฌ์ธํธ ์์ |
์์ธ์ฒ๋ฆฌ ์ ๋ต | ์ํฉ๋ณ ํ๋ฆ์ ๋ถ๊ธฐํ์ฌ ๋์ ์ค๊ณ ๊ฐ๋ฅ |
/refresh
API ์ค๊ณ์ ๋ ํ์ ์ ๊ฐ์ง ์ ์์๋ค