JWT(JSON Web Token)은 웹 어플리케이션과 서버 간의 정보를 안전하게 전달하기 위한 표준 중 하나.
JWT 는 클레임(Claim)을 JSON 객체로 표현하고, JSON 객체를 Base64로 인코딩하여 문자열로 만든 형태로 토큰을 생성한다. 서버에서 여기에 서명하여 인증 정보도 포함하게 된다.
JWT.IO - JSON Web Tokens Introduction
JWT는 크게 3 가지 단계로 구성된다.
Authorization
헤더에 포함하여 전달한다. 서버는 이 JWT 토큰을 검증하여 클라이언트의 인증 상태를 확인한다. 이때 확인하는 것은 다음과 같다.JWT 는 다음과 같은 구조를 가지고 있다.
Header.Payload.Signature
헤더는 일반적으로 두 부분으로 구성된다.
{
"alg": "HS256",
"typ": "JWT"
}
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}
JWT 의 두번째 부분은 Payload 로 클레임들을 포함하고 있다.
클레임은 JWT 에 포함되어지는 데이터를 말한다. JWT 는 Payload 에 클레임 정보를 JSON 형태로 저장하고 있다. 이 클레임 정보는 서버와 클라이언트 사이에 교환되는 데이터를 말한다.
Payload 에는 3 가지의 클레임이 있다.
"iss"
(Issuer): 토큰을 발행한 발행자(서버)의 식별자"sub"
(Subject): 토큰에 대한 주제(일반적으로 유저 ID 등)"aud"
(Audience): 토큰의 대상자(토큰을 사용할 대상)"exp"
(Expiration Time): 토큰의 만료 시간"nbf"
(Not Before): 토큰의 사용 가능 시작 시간"iat"
(Issued At): 토큰이 발급된 시간"jti"
(JWT ID): JWT의 고유 식별자둘의 유일한 차이점은 Public Claims 는 충돌방지를 해줘야하지만 Private Claims 은 그럴 필요가 없다는 점이다.
What is difference between private and public claims on jwt
서명 부분을 생성하려면 인코딩된 헤더, 인코딩된 페이로드, 비밀 키 값(secret), 헤더에 정의한 알고리즘 정보가 필요하다.
예를 들어 HMAC SHA256 알고리즘을 사용하려는 경우 서명은 다음과 같은 방식으로 생성된다.
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
이렇게 만들어진 인코딩 정보들을 합치면 다음과 같다.
각각의 인코딩 정보들은 .
로 구분된다.
서버에서 토큰을 생성하고 유저의 인증 정보를 파악하기 위해 비밀 설정 키를 사용한다. 클라이언트는 이 비밀 설정키를 모르기 때문에 토큰을 변조할 수 없다.
JWT 를 사용하면 서버에서 세션을 사용해 관리할 필요 없이 클라이언트 측에서 토큰을 저장하고 관리함으로써 서버의 확장성을 높일 수 있다.
JTW 는 표준화가 잘 되어 있어 여러 언어와 플랫폼에서 쉽게 사용할 수 있고 OAuth 와 같은 다른 인증 방식과 잘 통합되어 사용된다.
implementation group: 'io.jsonwebtoken', name: 'jjwt-api', version: '0.11.2'
runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-impl', version: '0.11.2'
runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-jackson', version: '0.11.2'
private final String SECRETE_KEY = "암호화 키 값";
private static final long EXPIRATION_TIME = 86400000; // 토큰의 만료 시간 (24시간)
public String createJwt(String param) {
Date now = new Date();
Date expirationDate = new Date(now.getTime() + EXPIRATION_TIME);
// 헤더
Map<String, Object> headers = new HashMap<>();
headers.put("typ", "JWT");
headers.put("alg", "HS256");
// 클레임 설정
Map<String, Object> claims = new HashMap<>();
claims.put("param", param);
Key key = Keys.hmacShaKeyFor(SECRETE_KEY.getBytes());
String jwt = Jwts.builder()
.setHeader(headers)
.setClaims(claims)
.setIssuedAt(now)
.setExpiration(expirationDate)
.signWith(key, SignatureAlgorithm.HS256)
.compact();
return jwt;
}
1000 * 60 * 60 * 10
는 1시간이다.SECRETE_KEY
를 너무 짧게 적으면 아래와 같은 에러가 발생한다.signWith
을 사용하는 방식이 수정되어서 위와 같이 작성했다.
[Spring Security + Kotlin] Jwts signWith deprecated 오류
public Map<String, Object> checkJwt(String jwt) throws UnsupportedEncodingException {
Map<String, Object> claimMap = null;
try {
Claims claims = Jwts.parser()
.setSigningKey(KEY.getBytes("UTF-8")) // 키 설정
.parseClaimsJws(jwt) // jwt의 정보를 파싱해서 시그니처 값을 검증한다.
.getBody();
claimMap = claims;
} catch (ExpiredJwtException e) { // 토큰이 만료되었을 경우
System.out.println(e);
} catch (Exception e) { // 그 외의 예외 경우
System.out.println(e);
}
return claimMap;
}
이런 유용한 정보를 나눠주셔서 감사합니다.