JWT 토큰
in Web-Programming on Spring
JWT
Json Web Token, Json 기반의 컴팩트한 토큰 방식으로 클라이언트와 서버 간의 안전하게 정보를 교환할 수 있도록 설계된 것이다. 주로 사용자 인증과 권한 부여에 사용되며, 한 번 로그인 후 클라이언트는 발급된 토큰을 서버에 전달하여 추가 인증 없이 리소스에 접근할 수 있다.
왜 등장했나?
여러 대 서버 운영시 세션마다 다른 로그인 정보 소유한 경우를 가정한다. 여기서 문제는 Client 1의 로그인 정보를 가지고 있지 않은 Sever2 나 Server3 에 API 요청 시 발생한 경우이다.
이럴 경우 클라이언트마다 요청 서버 고정하거나, 따로 세션 저장소 생성하여 모든 세션을 저장하는 방법이 있다. 그러나, 위 방법들은 설정해주는 과정이 귀찮은 방법들이다. 그래서 나온게 JWT 방법이다.
로그인 정보를 서버에 저장하지 않고 클라이언트에 JWT로 암호화하여 저장하며 이 때, 서버는 동일한 시크릿 키를 소유해야 하고 이는 암호화 및 위조 검증에 사용이 된다.
사용 흐름
- 로그인: 클라이언트가 사용자 자격 증명을 서버에 전송.
- 토큰 발급: 서버는 자격 증명을 검증한 후 JWT를 생성하여 클라이언트에 전달.
- 토큰 저장: 클라이언트는 JWT를 로컬 스토리지, 쿠키 등 안전한 위치에 저장.
- 인증 요청: 클라이언트는 보호된 리소스에 접근할 때 JWT를 HTTP 헤더에 포함하여 요청.
- 토큰 검증: 서버는 요청받은 JWT의 서명을 검증하고, 유효한 경우 해당 요청을 처리.
사용법
아래는 JWT 사용 코드이다. 해당 코드로 JWT를 생성하고 이를 이용하여 은증하고 확인할 수 있다.
/**
* Create JWT
*
* @param username
* @param role
* @return
*/
public String createJwt(String username, UserRole role) {
Date date = new Date();
Date expirationDate = new Date(date.getTime() + expirationTime);
return BEARER_PREFIX +
Jwts.builder()
.setSubject(username) // 사용자 식별자값(ID)
.claim(AUTHORIZATION_KEY, role) // 사용자 권한
.setExpiration(expirationDate) // 만료 시간
.setIssuedAt(date) // 발급일
.signWith(key, signatureAlgorithm) // 암호화 알고리즘
.compact();
}
/**
* get JWT from header
*
* @param request
* @return
*/
public String getJwtFromHeader(HttpServletRequest request) {
String bearerToken = request.getHeader(AUTHORIZATION_HEADER);
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith(BEARER_PREFIX)) {
return bearerToken.substring(BEARER_PREFIX.length());
}
return null;
}
/**
* Valicate JWT
*
* @param token
* @return
*/
public boolean validateToken(String token) {
if (isTokenBlacklisted(token)) {
log.error("JWT token is blacklisted");
return false;
}
try {
Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token);
return true;
} catch (SecurityException | MalformedJwtException | SignatureException e) {
log.error("Invalid JWT signature");
} catch (ExpiredJwtException e) {
log.error("Expired JWT token");
} catch (UnsupportedJwtException e) {
log.error("Unsupported JWT token");
} catch (IllegalArgumentException e) {
log.error("JWT claims is empty");
}
return false;
}
/**
* get User Info from JWT
*
* @param token
* @return
*/
public Claims getUserInfoFromToken(String token) {
return Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token).getBody();
}
이와 같이 JWT를 생성하고 검증하는 로직을 작성한 후, Spring Security 필터 체인에서 인증 및 권한 부여에 활용할 수 있다.