๐
๋ ์ง
2025-05-20
๐ ํ์ต ๋ด์ฉ
1๏ธโฃ JWT ์ธ์ฆ ์ ์ฒด ํ๋ฆ ์์ฝ
๋จ๊ณ | ์ค๋ช
|
---|
๋ก๊ทธ์ธ ์ฑ๊ณต | AT, RT ๋ฐ๊ธ โ AT: ํด๋ผ์ด์ธํธ ์ฟ ํค / RT: ์๋ฒ(DB) ์ ์ฅ |
์ธ์ฆ ์์ฒญ | ์ฟ ํค์ AccessToken โ JwtAuthorizationFilter ์์ ๊ฒ์ฆ |
AT ๋ง๋ฃ | RT ์ ํจ ์ ์๋ก์ด AT ์ฌ๋ฐ๊ธ (์ฟ ํค+DB ๊ฐฑ์ ) |
RT ๋ง๋ฃ | ์ฟ ํค ์ญ์ + DB ์ญ์ + ์ฌ๋ก๊ทธ์ธ ํ์ |
๋ก๊ทธ์์ | ์ฟ ํค ์ญ์ + DB์์ AT ์ญ์ + ์์
๋ก๊ทธ์์ ์ง์ |
2๏ธโฃ CustomLoginSuccessHandler
โ ๋ก๊ทธ์ธ ์ฒ๋ฆฌ ์ ์ฒด ์๋๋ฆฌ์ค
@Slf4j
@Component
public class CustomLoginSuccessHandler 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);
Cookie cookie = new Cookie(JwtProperties.ACCESS_TOKEN_COOKIE_NAME, tokenInfo.getAccessToken());
cookie.setMaxAge(JwtProperties.REFRESH_TOKEN_EXPIRATION_TIME);
cookie.setPath("/");
response.addCookie(cookie);
JwtToken jwtToken = JwtToken.builder()
.accessToken(tokenInfo.getAccessToken())
.refreshToken(tokenInfo.getRefreshToken())
.username(authentication.getName())
.createAt(LocalDateTime.now())
.build();
jwtTokenRepository.save(jwtToken);
response.sendRedirect(request.getContextPath() + "/");
}
}
3๏ธโฃ AccessToken ๋ง๋ฃ โ RefreshToken ์ฌ๋ฐ๊ธ (JwtAuthorizationFilter
)
if (jwtTokenProvider.validateToken(refreshToken)) {
String newAccessToken = ...
response.addCookie(new Cookie(..., newAccessToken));
jwtToken.setAccessToken(newAccessToken);
jwtTokenRepository.save(jwtToken);
} else {
}
๐ AT ์ฌ๋ฐ๊ธ ํ๋ฆ ๋์:
[์์ฒญ] โ [AT ๋ง๋ฃ ํ์ธ]
โ
[RT ์กด์ฌ?] โ โ ์ฌ๋ก๊ทธ์ธ
โ
[RT ์ ํจ?] โ โ ์ฌ๋ก๊ทธ์ธ
โ
โ
โ ์ AT ๋ฐ๊ธ โ ์ฟ ํค ๊ฐฑ์ + DB ์
๋ฐ์ดํธ
4๏ธโฃ ๋ก๊ทธ์์ ์ฒ๋ฆฌ (CustomLogoutSuccessHandler
)
๋จ๊ณ | ์ค๋ช
|
---|
1๏ธโฃ AccessToken ์ฟ ํค์์ ์ถ์ถ | |
2๏ธโฃ ํด๋น AT DB์์ ์ญ์ (deleteByAccessToken ) | |
3๏ธโฃ ํด๋ผ์ด์ธํธ ์ฟ ํค ์ญ์ (MaxAge=0 ) | |
4๏ธโฃ ์์
๋ก๊ทธ์ธ ๋ถ๊ธฐ ์ฒ๋ฆฌ (Kakao, Naver, Google) | |
5๏ธโฃ ์ผ๋ฐ ์ฌ์ฉ์ ๋ฉ์ธ ํ์ด์ง๋ก ์ด๋ | |
๐ ๋ณด์ ํ โ ์ฟ ํค ์ค์
์ค์ ํญ๋ชฉ | ์ค๋ช
|
---|
HttpOnly | JavaScript ์ ๊ทผ ์ฐจ๋จ (XSS ๋ฐฉ์ง) |
Secure | HTTPS์์๋ง ์ ์ก |
SameSite=Lax | CSRF ๋ฐฉ์ด (ํน์ Strict ) |
Path="/" | ๋ฃจํธ ์ ์ฒด์์ ์ฟ ํค ์ ์ฉ |
cookie.setHttpOnly(true);
cookie.setSecure(true);
cookie.setPath("/");
๐งช ํ
์คํธ ํ
- Postman์์ AT ์์ด ์์ฒญ โ 401 ์๋ต ํ์ธ
- ๋ธ๋ผ์ฐ์ ๊ฐ๋ฐ์ ๋๊ตฌ > Application > Cookie ํญ์์ ์๋ ๊ฐฑ์ ์ฌ๋ถ ํ์ธ
- ๋ก๊ทธ์์ ํ ์ฟ ํค ๋ฐ DB ์ญ์ ์ฌ๋ถ ์ฝ์ ํ์ธ
๐งฉ ์์ธ ์ํฉ ์์ฝ
์ํฉ | ์ค๋ช
| ์ฒ๋ฆฌ |
---|
AT โ / RT โ | ์ต์ด ๋ก๊ทธ์ธ | AT ๋ฐ๊ธ + ์ ์ฅ |
AT โ
/ RT โ
| ์ ์ ํ๋ฆ | ๋ก๊ทธ์ธ ์๋ฃ |
AT โ / RT โ
| ์ฟ ํค ์ญ์ ๋จ | AT ์ฌ๋ฐ๊ธ |
AT โ
/ RT โ | ์์ธ ํ๋ฆ | ๋น์ ์ ํ ํฐ (๋ก๊ทธ์์ ๊ถ์ฅ) |
AT, RT ๋ชจ๋ ๋ง๋ฃ | ์ธ์
์ข
๋ฃ | ์ฟ ํค/DB ์ ๊ฑฐ, ์ฌ๋ก๊ทธ์ธ ์ ๋ |
โ
์ด ๊ธ์์ ๋ฐฐ์ด ๊ฒ
- โ
JWT ๊ธฐ๋ฐ ์ธ์ฆ์์ ํ ํฐ ์ฌ๋ฐ๊ธ๊ณผ ๋ก๊ทธ์์ ํ๋ฆ์ ์์ฑ
- โ
RefreshToken ์ ์ฅ์ผ๋ก ์ฌ๋ก๊ทธ์ธ ์ต์ํ, ๋ณด์ ์ ์ด ๊ฐ๋ฅ
- โ
์์ธ ์ํฉ์ ์ ๋ฆฌํจ์ผ๋ก์จ ์ค๋ฌด ๋์ ์ค๊ณ ๋ฅ๋ ฅ ํฅ์
๐ง ๋๋ ์
- ์ ์ฒด ์ธ์ฆ ํ๋ฆ์ ์ง์ ์ค๊ณํ๋ฉฐ ์ค๋ฌด ์์ค์ ์ดํด๋๊ฐ ํ์คํ ์ฌ๋ผ๊ฐ๋ค.
- JWT ์ธ์ฆ์ ๋จ์ ๋ฐ๊ธ์ด ์๋ ์ํ ๊ด๋ฆฌ, ๋ง๋ฃ ๋์, ์์ธ ์ฒ๋ฆฌ๊น์ง ์ค๊ณ๊ฐ ์๋ช
์์ ์ฒด๊ฐํ๋ค.
- ๋ค์์ผ๋ก๋ Redis ๊ธฐ๋ฐ RefreshToken ์ ์ฅ๊ณผ
/refresh
API๋ฅผ RestController๋ก ๋ถ๋ฆฌํด๋ณด๊ณ ์ถ๋ค.
โ
์์ฝ
- JWT ์ธ์ฆ ์์คํ
์ ๊ตฌ์ฑํ ๋ ๋ฐ๋์ ํ ํฐ ๋ฐ๊ธ โ ์ธ์ฆ โ ์ฌ๋ฐ๊ธ โ ๋ก๊ทธ์์๊น์ง ์ค๊ณํด์ผ ํ๋ค.
- RefreshToken์ ์๋ฒ์์ ๊ด๋ฆฌ๋๋ฉฐ, AccessToken ๋ง๋ฃ ์์ ๋ณด์์ฑ๊ณผ UX๋ฅผ ๋์์ ๋ณด์ฅํ๋ค.
- ์์ฑ๋ ๊ตฌ์กฐ๋ ํ์ฅ ๊ฐ๋ฅํ ์ธ์ฆ ์์คํ
์ ๊ธฐ๋ฐ์ด ๋๋ค.