JWT ๊ธฐ๋ฐ ์ธ์ฆ
- ์ด์ค ํ ํฐ ๊ตฌ์กฐ: Access Token (24์๊ฐ) + Refresh Token (7์ผ)
- HS256 ์๋ช
์๊ณ ๋ฆฌ์ฆ์ผ๋ก ํ ํฐ ๋ฌด๊ฒฐ์ฑ ๋ณด์ฅ
- ๋ฉํฐ ํ
๋ํธ ์ง์์ผ๋ก ์กฐ์ง๋ณ ๋
๋ฆฝ์ ์ธ ์ธ์ฆ ๊ด๋ฆฌ
- ์๋ ํ ํฐ ๊ฐฑ์ ์ผ๋ก ๋๊น ์๋ ์ฌ์ฉ์ ๊ฒฝํ
// JWT ํ ํฐ ์์ฑ
public String createAccessToken(Long userId, Role role, String tenantId) {
Map<String, Object> claims = new HashMap<>();
claims.put("userId", userId);
claims.put("role", role.name());
claims.put("tenantId", tenantId);
return Jwts.builder()
.claims(claims)
.signWith(getSigningKey(), Jwts.SIG.HS256)
.compact();
}
๊ณ์ ์ ๊ธ (๋ก๊ทธ์ธ ์๋ ์ ํ)
- ๋น๋ฐ๋ฒํธ ํด์ฑ: BCrypt๋ฅผ ํตํ ์์ ํ ๋น๋ฐ๋ฒํธ ์ ์ฅ
- ์ด์ค ๋ก๊ทธ์ธ ๋ฐฉ์ง: Refresh Token Rotation์ผ๋ก ๋ณด์ ๊ฐํ
- ํ ํฐ ๋ธ๋๋ฆฌ์คํธ: ๋ก๊ทธ์์ ์ ์ฆ์ ํ ํฐ ๋ฌดํจํ
- ์๋ ํ ํฐ ์ ๋ฆฌ: ๋ง๋ฃ๋ ํ ํฐ ์๋ ์ ๊ฑฐ
// Refresh Token Rotation
public LoginResult refreshToken(String refreshToken) {
Long userId = jwtTokenService.validateAndGetUserIdFromRefreshToken(refreshToken);
// ์๋ก์ด ํ ํฐ ์ ์์ฑ
String newAccessToken = jwtTokenService.createAccessToken(userId, userRole, null);
String newRefreshToken = jwtTokenService.createRefreshToken(userId);
// ๊ธฐ์กด ํ ํฐ์ ์๋ก์ด ๊ฒ์ผ๋ก ๊ต์ฒด
saveOrUpdateRefreshToken(userId, newRefreshToken);
return LoginResult.builder()
.accessToken(newAccessToken)
.refreshToken(newRefreshToken)
.build();
}
๊ถํ ๋ ๋ฒจ ๊ด๋ฆฌ
- ์ญํ ๊ธฐ๋ฐ ์ ๊ทผ ์ ์ด (RBAC): USER, ADMIN ๊ถํ ์ฒด๊ณ
- ๊ณ์ธต์ ๊ถํ ๊ตฌ์กฐ: ๋์ ๊ถํ์ด ๋ฎ์ ๊ถํ์ ์๋ ํฌํจ
- ๋ฉ์๋ ๋ ๋ฒจ ๋ณด์:
@PreAuthorize ์ด๋
ธํ
์ด์
์ผ๋ก ์ธ๋ฐํ ๊ถํ ์ ์ด
// ๊ถํ ์ฒด๊ณ
public enum Role {
USER("์ผ๋ฐ์ฌ์ฉ์"),
ADMIN("๊ด๋ฆฌ์");
public boolean hasPermission(Role requiredRole) {
return this.ordinal() >= requiredRole.ordinal();
}
public boolean isAdmin() {
return this.ordinal() >= ADMIN.ordinal();
}
}
๋ง์ง๋ง ๋ก๊ทธ์ธ ์๊ฐ ์ถ์
- ์ค์๊ฐ ๋ก๊ทธ์ธ ๊ธฐ๋ก: ๋ชจ๋ ๋ก๊ทธ์ธ ์๋ ์๋ ๊ธฐ๋ก
- ์ฌ์ฉ์ ํ๋ ๋ชจ๋ํฐ๋ง:
lastLoginAt ํ๋๋ก ์ต๊ทผ ํ๋ ์ถ์