728x90
Enum Class = UserRole
public enum UserRole {
USER(Authority.USER), // 사용자 권한
ADMIN(Authority.ADMIN); // 관리자 권한
private final String authority;
UserRole(String authority) {
this.authority = authority;
}
public String getAuthority() {
return this.authority;
}
public static class Authority {
public static final String USER = "ROLE_USER";
public static final String ADMIN = "ROLE_ADMIN";
}
}
JwtUtil 멤버 변수 선언
@Component
public class JwtUtil {
// Header KEY 값
public static final String AUTHORIZATION_HEADER = "Authorization";
// 사용자 권한 값의 KEY
public static final String AUTHORIZATION_KEY = "auth";
// Token 식별자
public static final String BEARER_PREFIX = "Bearer ";
// 토큰 만료시간(ms)
private final long TOKEN_TIME = 30 * 60 * 1000L; // 30분
// Base64 Encode 한 SecretKey
@Value("${jwt.secret.key}")
private String secretKey;
private Key key;
private final SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
// 로그 설정
public static final Logger logger = LoggerFactory.getLogger("JWT 관련 로그");
}
- AUTHORIZATION_HEADER = "Authorization"
- Cookie의 Name으로 설정할 값(Key값)
- AUTHROZATION_KEY = "auth"
- JWT의 Payload의 권한의 key값
{
"auth" : "user"
}
- BEARER_PREFIX = "Bearer "
- JWT Token의 Value 앞에 붙는 접두사
- 반드시 Bearer로 설정할 필요는 없으나, 'Bearer(스페이스)'로 사용하는 것이 일종의 약속
- TOKEN_TIME = 30 * 60 * 1000L
- JWT의 만료 시간(30분으로 설정)
- 30 = 분 / 60 = 초 / 1000 = ms초
- secretKey
- (@Value = ${jwt.secret.key}) ⇒ application.yml 또는 properties에 선언한 환경 변수
- JWT 서명(Signature)을 생성하고 검증할 때 사용되는 비밀 키(secret key)
- signatureAlgorithm = SignatureAlgorithm.HS256
- 암호화 알고리즘 선택
- SignatureAlgorithm은 Enum으로 구현
public enum SignatureAlgorithm {
NONE("none", "No digital signature or MAC performed", "None", (String)null, false, 0, 0),
HS256("HS256", "HMAC using SHA-256", "HMAC", "HmacSHA256", true, 256, 256, "1.2.840.113549.2.9"),
HS384("HS384", "HMAC using SHA-384", "HMAC", "HmacSHA384", true, 384, 384, "1.2.840.113549.2.10"),
HS512("HS512", "HMAC using SHA-512", "HMAC", "HmacSHA512", true, 512, 512, "1.2.840.113549.2.11")
// 생략
}
- logger
- 로깅을 위한 변수
- 대신 Slf4j를 사용해도 됨
init 메서드
@PostConstruct
public void init() {
byte[] bytes = Base64.getDecoder().decode(secretKey);
key = Keys.hmacShaKeyFor(bytes);
}
- @PostConstruct
- 의존성 주입이 이루어진 뒤 초기화를 수행하는 메서드
- 객체가 완전히 초기화된 후에 추가적인 설정이나 초기화 작업을 수행하기 위해 사용
- Spring Context에 빈으로 등록 → 의존성 주입 → 자동으로 @PostConstruct가 붙은 메서드 실행
addJwtToCookie 메서드
public String createToken(String username, UserRole 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();
}
- setSubject(String s)
- 역할의 주체 설정
→ 보통 사용자의 식별자(userName, userId 등) - JWT에서의 의미 : "sub" 클레임에 해당
- 역할의 주체 설정
{
"sub": "username"
}
- claim(String name, Object value)
- 토큰에 커스텀 클레임을 추가
- JWT에서의 의미 : JWT payload의 key-value 쌍으로 토큰에 포함될 추가적인 정보를 정의
{
"auth": "ROLE_USER"
}
- setExpiration(Date expiration)
- 토큰의 만료 시간 설정
- JWT에서의 의미 : "exp" 클레임에 해당
→ 해당 시간을 지나면 유효하지 않은 토큰이 됨
{
"exp": 1693412185
}
- setIssuedAt(Date issuedAt)
- 토큰이 발급된 시간 설정
- JWT에서의 의미 : "iat" 클레임에 해당
→ 토큰이 언제 생성되었는지를 나타냄, 주로 만료 시간과 함께 사용
{
"iat": 1693408585
}
- signWith(Key key, SignatureAlgorithm algorithm)
- 지정한 키와 알고리즘을 사용하여 JWT에 서명을 추가
→ 토큰의 위변조를 방지하는 데 사용 - JWT에서의 의미 : 서명(Signature) 부분에 해당
→ JWT의 무결성을 보장
- 지정한 키와 알고리즘을 사용하여 JWT에 서명을 추가
- compact()
- 설정된 모든 정보를 바탕으로 JWT를 생성하여 문자열 반환
- JWT에서의 의미 : 앞서 설정한 헤더, 페이로드, 서명을 하나의 JWT 토큰으로 압축(Compact)하여 최종 결과를 반환
addJwtToCookie 메서드
- Spring WebFlux의 ServerWebExchange를 사용하여 비동기식 웹 응답을 다루는 코드
public void addJwtToCookie(String token, ServerWebExchange exchange) {
ResponseCookie cookie = ResponseCookie.from(AUTHORIZATION_HEADER, token)
.path("/")
.httpOnly(true)
.build();
exchange.getResponse().addCookie(cookie);
}
- ResponseCookie.from(String name, String value)
- 객체를 생성하는 빌더 메서드
- key-value 형태로 '이름 : 값'을 지정
- .path("/")
- 쿠키의 경로 설정
- 쿠키가 유효한 URL 패턴을 정의하며, 이 경로 이하의 모든 요청에서 쿠키가 전송
- /는 애플리케이션의 모든 경로에서 쿠키를 사용할 수 있도록 설정
- .httpOnly(true)
- 쿠키의 HttpOnly 속성을 설정
- 속성이 true일 경우 JavaScript와 같은 클라이언트 사이드 스크립트에서 쿠키에 접근 불가
- XSS(교차 사이트 스크립팅) 공격으로부터 쿠키를 보호하는 데 도움
- exchange.getResponse()
- 현재 HTTP 응답 객체를 가져옴 (HttpServletResponse와 비슷한 역할)
- 이 객체를 사용하여 응답에 쿠키를 추가하거나 다른 작업을 수행 가능
- .addCookie(ResponseCookie cookie)
- 응답 객체에 쿠키를 추가
- 이 메서드는 ResponseCookie 객체를 받아서 응답에 포함
subStringToken 메서드
public String substringToken(String tokenValue) {
if (StringUtils.hasText(tokenValue) && tokenValue.startsWith(BEARER_PREFIX)) {
return tokenValue.substring(7);
}
logger.error("Not Found Token");
throw new NullPointerException("Not Found Token");
}
- StringUtils.hasText(String str)
- str이 null이거나 공백이 아닌 지 확인
- tokenValue.startWith(String prefix)
- 토큰이 "Bearer "로 시작하는 지 확인
- tokenValue.substring(7)
- "Bearer "로 시작하기 때문에 떼고, 뒤의 토큰 내용만 반환
validateToken 메서드
public boolean validateToken(String token) {
try {
Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token);
return true;
} catch (SecurityException | MalformedJwtException | SignatureException e) {
logger.error("Invalid JWT signature, 유효하지 않는 JWT 서명 입니다.");
} catch (ExpiredJwtException e) {
logger.error("Expired JWT token, 만료된 JWT token 입니다.");
} catch (UnsupportedJwtException e) {
logger.error("Unsupported JWT token, 지원되지 않는 JWT 토큰 입니다.");
} catch (IllegalArgumentException e) {
logger.error("JWT claims is empty, 잘못된 JWT 토큰 입니다.");
}
return false;
}
- Jwts.parserBuilder()
- JWT 파서를 생성하기 위한 빌더를 반환
- JWT를 파싱할 때 사용할 다양한 설정을 구성 가능
- setSigningKey(key)
- JWT를 검증하는 데 사용할 서명 키를 설정
- 이 키는 JWT가 서명될 때 사용된 것과 동일해야 하며, 이를 통해 JWT의 서명이 유효한지 확인 가능
- key는 JWT 서명에 사용되는 비밀 키로 JwtUtil 클래스에서 @PostConstruct를 통해 초기화된 Key 객체
- build()
- build() 메서드는 설정된 빌더를 사용하여 JwtParser 객체를 생성
- JwtParser는 실제 JWT를 파싱하고 검증하는 기능을 제공
- parseClaimsJws(token)
- parseClaimsJws(token)는 주어진 token을 파싱하여 JWT의 서명과 유효성을 검증
- parseClaimsJws 메서드는 유효한 JWT를 포함하는 Jws<Claims> 객체를 반환
- 이 객체는 JWT의 서명이 유효한지와 JWT의 페이로드(claims)를 포함
- 이 메서드는 JWT의 서명이 올바르지 않거나 만료된 경우, 또는 다른 검증 오류가 발생한 경우 예외 throw
getUserInfoFromToken 메서드
public Claims getUserInfoFromToken(String token) {
return Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token).getBody();
}
- getBody()
- JWT의 payload(클레임)를 반환하는 메서드
- Jws<Claims> 객체에서 클레임(payload) 부분을 추출하여 반환
- 반환되는 Claims 객체는 JWT의 클레임을 포함하며, 클레임은 JWT의 payload에 저장된 데이터
- Ex) 사용자 정보, 권한, 만료 시간 등 다양한 정보를 담고 있을 수 있음
728x90
'Language > Spring' 카테고리의 다른 글
[Redis] 캐시란? (0) | 2024.09.07 |
---|---|
[JWT] Refresh Token을 이용한 Access Token 재발급 (0) | 2024.08.28 |
[JWT] JWT 등장 배경, Access Token, Refresh Token (0) | 2024.08.27 |
[Authentication] Cookie, Session, JWT Token (0) | 2024.08.26 |
[Spring Boot] 일정 주기마다 업데이트 프로젝트 적용해보기 (0) | 2024.08.19 |