JWT는 JSON 포맷을 이용하여 사용자에 대한 속성을 저장하는 Claim 기반의 Web Token으로, 토큰의 한 종류입니다. 일반적으로 쿠키 저장소를 사용하여 JWT를 저장합니다.
Session1이 모든 클라이언트의 로그인 정보를 소유하고 있습니다.
서버의 대용량 트래픽 처리를 위해 서버 2대 이상 운영이 필요할 수 있습니다. 각 서버마다 다른 클라이언트 로그인 정보를 가지고 있을 수 있습니다.
만약 Client1의 로그인 정보를 가지고 있지 않은 Server2나 Server3에 API 요청을 하게 되면 문제가 발생할 수 있습니다.
로그인 정보를 서버에 저장하지 않고, 클라이언트에 로그인 정보를 JWT로 암호화하여 저장합니다. 이를 통해 인증/인가를 수행합니다. 모든 서버에서 동일한 Secret Key를 소유하며, Secret Key를 통해 암호화 및 위조 검증(복호화 시)을 수행합니다.
username
, password
로 로그인 성공 시, 서버에서 "로그인 정보"를 JWT로 암호화합니다. (Secret Key 사용)JWT는 누구나 평문으로 복호화 가능합니다. 하지만 Secret Key가 없으면 JWT를 수정할 수 없습니다. 결국 JWT는 읽기 전용 데이터입니다.
예시:
{
"alg": "HS256",
"typ": "JWT"
}
{
"sub": "1234567890",
"username": "카즈하",
"admin": true
}
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret
)
Payload에 실제 유저의 정보가 들어있고, HEADER와 VERIFY SIGNATURE 부분은 암호화 관련된 정보 양식입니다.
JWT 관련 기능을 수행하기 위해 JwtUtil
이라는 유틸리티 클래스를 구현했습니다.
public String createToken(String username, UserRoleEnum 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();
}
public void addJwtToCookie(String token, HttpServletResponse res) {
try {
token = URLEncoder.encode(token, "utf-8").replaceAll("\\+", "%20"); // Cookie Value 에는 공백이 불가능해서 encoding 진행
Cookie cookie = new Cookie(AUTHORIZATION_HEADER, token); // Name-Value
cookie.setPath("/");
// Response 객체에 Cookie 추가
res.addCookie(cookie);
} catch (UnsupportedEncodingException e) {
logger.error(e.getMessage());
}
}
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;
}
public Claims getUserInfoFromToken(String token) {
return Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token).getBody();
}
이번 학습을 통해 JWT를 이용한 인증 방식에 대해 이해하고, 이를 구현하는 방법을 배울 수 있었습니다. JWT를 이용하면 서버 측 세션 관리를 하지 않고도 인증을 처리할 수 있어 서버의 부하를 줄이는 데 유용하다는 점이 인상적이었습니다.