JWT의 핵심 개념
JSON Web Token(JWT)은 애플리케이션 간에 안전하게 정보를 전달하기 위한 표준 형식입니다. 주로 사용자 인증, 정보 공유 및 권한 부여 등에 활용되며, 서버 상태를 유지하지 않는 무상태 아키텍처에서 적합합니다.
JWT의 구성 요소
- 헤더(Header): 토큰 유형과 서명 알고리즘을 정의합니다. 예:
{"typ": "JWT", "alg": "RS256"} - 페이로드(Payload): 실제 데이터가 포함된 영역으로, 다음 세 가지 종류의 클레임을 포함할 수 있습니다:
- 기본 클레임 (예:
iss,exp,sub) - 공개 클레임 (개발자가 정의하는 커스텀 값)
- 비공개 클레임 (특정 서비스 간에 공유되는 내부 정보)
- 기본 클레임 (예:
- 서명(Signature): 헤더와 페이로드를 결합하여 생성되며, 메시지의 무결성을 보장합니다. 서명은 비밀키로 생성되고, 공개키로 검증됩니다.
키 쌍 생성 및 토큰 발급
암호화를 위해 RSA 기반의 키 쌍을 생성하는 예제 코드입니다.
public class KeyPairGenerator {
public static void main(String[] args) throws JoseException {
RsaJsonWebKey key = RsaJwkGenerator.generateJwk(2048);
System.out.println("공개키:\n" + key.toJson(JsonWebKey.OutputControlLevel.PUBLIC_ONLY));
System.out.println("비밀키:\n" + key.toJson(JsonWebKey.OutputControlLevel.INCLUDE_PRIVATE));
}
}
JWT 발행 도구 클래스
사용자 식별자와 이름을 기반으로 JWT를 생성하고 검증하는 유틸리티 클래스입니다.
public class TokenService {
private static final String PRIVATE_KEY_JWK = "{...}"; // 비밀키 문자열
private static final String PUBLIC_KEY_JWK = "{...}"; // 공개키 문자열
public static String generateToken(Long userId, String userName) throws Exception {
JwtClaims claims = new JwtClaims();
claims.setIssuer("myapp");
claims.setAudience("api");
claims.setExpirationTimeMinutesInTheFuture(1440); // 24시간
claims.setGeneratedJwtId();
claims.setIssuedAtToNow();
claims.setNotBeforeMinutesInThePast(1);
claims.setSubject("user-auth");
claims.setClaim("id", userId);
claims.setClaim("name", userName);
JsonWebSignature jws = new JsonWebSignature();
jws.setPayload(claims.toJson());
jws.setKey(new RsaJsonWebKey(JsonUtil.parseJson(PRIVATE_KEY_JWK)).getPrivateKey());
jws.setKeyIdHeaderValue("key-1");
jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256);
return jws.getCompactSerialization();
}
public static Map<String, Object> parseToken(String token) {
try {
PublicKey publicKey = new RsaJsonWebKey(JsonUtil.parseJson(PUBLIC_KEY_JWK)).getPublicKey();
JwtConsumer consumer = new JwtConsumerBuilder()
.setRequireExpirationTime()
.setAllowedClockSkewInSeconds(30)
.setRequireSubject()
.setExpectedIssuer("myapp")
.setExpectedAudience("api")
.setVerificationKey(publicKey)
.setJwsAlgorithmConstraints(
new AlgorithmConstraints(AlgorithmConstraints.ConstraintType.WHITELIST, AlgorithmIdentifiers.RSA_USING_SHA256))
.build();
JwtClaims claims = consumer.processToClaims(token);
return claims.getClaimsMap();
} catch (Exception e) {
return new HashMap<>();
}
}
}
사용 예시
String token = TokenService.generateToken(1001L, "alice");
Map<String, Object> payload = TokenService.parseToken(token);
System.out.println(payload.get("id")); // 출력: 1001
System.out.println(payload.get("name")); // 출력: alice