JWT 발급 소스 코드
1
2
3
4
5
6
7
8
9
10
11
12
13
  |  private String generateToken(UserDetails userDetails){
    Date now = new Date();
    Date expirationDate = new Date(now.getTime() + 36000);
    return Jwts.builder()
        .setSubject(userDetails.getUsername())
        .setId("Time")
        .setIssuedAt(now)
        .setExpiration(expirationDate)
        .signWith(SignatureAlgorithm.HS512, SECRET_KEY)
        .compact();
}
 | 
JWT 발급은 이와 동일하게 갈것이고 우리는 여기에 디버그를 걸어서 어떻게 생성이 되는지 확인을 해볼것이다
DefaultJwtBuilder
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
  | public class DefaultJwtBuilder implements JwtBuilder 
public String compact() {
    if (this.serializer == null) {
        this.serializer = LegacyServices.loadFirst(Serializer.class);
    }
    if (payload == null && Collections.isEmpty(claims)) {
        payload = "";
    }
    if (payload != null && !Collections.isEmpty(claims)) {
        throw new IllegalStateException("Both 'payload' and 'claims' cannot both be specified. Choose either one.");
    }
    Header header = ensureHeader();
    JwsHeader jwsHeader;
    if (header instanceof JwsHeader) {
        jwsHeader = (JwsHeader) header;
    } else {
        jwsHeader = new DefaultJwsHeader(header);
    }
    if (key != null) {
        jwsHeader.setAlgorithm(algorithm.getValue());
    } else {
        jwsHeader.setAlgorithm(SignatureAlgorithm.NONE.getValue());
    }
    if (compressionCodec != null) {
        jwsHeader.setCompressionAlgorithm(compressionCodec.getAlgorithmName());
    }
    String base64UrlEncodedHeader = base64UrlEncode(jwsHeader, "Unable to serialize header to json.");
    byte[] bytes;
    try {
        bytes = this.payload != null ? payload.getBytes(Strings.UTF_8) : toJson(claims);
    } catch (SerializationException e) {
        throw new IllegalArgumentException("Unable to serialize claims object to json: " + e.getMessage(), e);
    }
    if (compressionCodec != null) {
        bytes = compressionCodec.compress(bytes);
    }
    String base64UrlEncodedBody = base64UrlEncoder.encode(bytes);
    String jwt = base64UrlEncodedHeader + JwtParser.SEPARATOR_CHAR + base64UrlEncodedBody;
    if (key != null) { 
        JwtSigner signer = createSigner(algorithm, key);
        String base64UrlSignature = signer.sign(jwt);
        jwt += JwtParser.SEPARATOR_CHAR + base64UrlSignature;
    } else {
        jwt += JwtParser.SEPARATOR_CHAR;
    }
    return jwt;
}
 | 
DefaultJwtBuilder jjwt 라이브러리에서 제공되는 클래스로 JWT를 생성하는 아주 기본적인 JWT 생성기입니다
1
2
3
4
5
6
7
  | if (payload == null && Collections.isEmpty(claims)) {
    payload = "";
}
if (payload != null && !Collections.isEmpty(claims)) {
    throw new IllegalStateException("Both 'payload' and 'claims' cannot both be specified. Choose either one.");
}
 | 
payload 가 null 이거나 claims 가 비어 있다면 payload= “” 로 초기화를 진행을 하는데 만약 payload 가 null 이 아니면서 claims 가 비어있지 않으면 Both ‘payload’ and ‘claims’ cannot both be specified. Choose either one. payload 와 claims 를 둘다 사용할 수 없습니다 둘중 하나만 선택을 해서 써야 합니다
payload 를 쓰는 예제
1
2
3
4
5
6
  | Jwts.builder()
    .setSubject("user123")
    .claim("name", "John Doe")
    .claim("admin", true)
    .signWith(secretKey)
    .compact();
 | 
Claims 를 쓰는 예제
1
2
3
4
5
6
7
8
  | Claims claims = Jwts.claims()
    .setSubject("user123")
    .setAudience("audience123");
Jwts.builder()
    .setClaims(claims)
    .signWith(secretKey)
    .compact();
 | 
즉 이 둘을 동시에 사용하지 말아야 한다는 뜻입니다
1
2
  | Header header = ensureHeader();
  | 
여기서 사용하는 헤더는 우리가 흔히 알고 있는 HttpHeader 이 아닌 public interface Header<T extends Header<T>> extends Map<String, Object>  마찬가지로 inserface Header 로 Map 을 상속을 받는것을 볼 수 있습니다 이 인터페이스의 구현체는
1
2
  | public class DefaultHeader<T extends Header<T>> extends JwtMap implements Header<T> 
  | 
가 구현을 하고 있으며 ensureHeader() 를 호출할때 DefaultHeader 를 return 하게 됩니다
1
2
3
4
5
6
7
  | protected Header ensureHeader() {
    if (this.header == null) {
        this.header = new DefaultHeader();
    }
    return this.header;
}
 | 
헤더에 암호화 알고리즘 세팅
1
2
3
4
5
6
7
  | if (key != null) {
    jwsHeader.setAlgorithm(algorithm.getValue());
} else {
    //no signature - plaintext JWT:
    jwsHeader.setAlgorithm(SignatureAlgorithm.NONE.getValue());
}
 | 
이때 암호화 알고리즘을 넣게 되는데 이는 우리가 위에서 Builder 를 만들때 사용한 .signWith(SignatureAlgorithm.HS512, SECRET_KEY) 이 부분을 사용하게 됩니다 그리고 만약 이때 선택한 알고리즘이 없으면 이때는 알고리즘을 선택하지는 않지만 마찬가지로 서명또한 없게 됩니다
String base64UrlEncodedHeader = base64UrlEncode(jwsHeader, "Unable to serialize header to json."); 그리고 위에서 만들어진 헤더를 base64로 인코딩해서 인코딩된 헤더를 만들게됩니다
Payload 만들기
1
2
3
4
5
6
7
8
9
10
11
12
  | byte[] bytes;
try {
    bytes = this.payload != null ? payload.getBytes(Strings.UTF_8) : toJson(claims);
} catch (SerializationException e) {
    throw new IllegalArgumentException("Unable to serialize claims object to json: " + e.getMessage(), e);
}
if (compressionCodec != null) {
    bytes = compressionCodec.compress(bytes);
}
String base64UrlEncodedBody = base64UrlEncoder.encode(bytes);
 | 
이제 payload 를 BASE64로 인코딩하는 모습입니다 안에 있는 claims 정보를 bytes 에 담고 그 정보를 base64UrlEncoder 로 마찬가지로 인코딩 하게 됩니다 그리고 이렇게 만들어진 정보는 앞의 헤더 부분과 .(점) 으로 구분되어서 헤더와 페이로드가 만들어지게 됩니다 String jwt = base64UrlEncodedHeader + JwtParser.SEPARATOR_CHAR + base64UrlEncodedBody;  이때 ` JwtParser.SEPARATOR_CHAR` 는 .(점) 을 뜻합니다
JWT 서명
1
2
3
4
5
6
7
8
9
10
11
12
  | if (key != null) { 
    JwtSigner signer = createSigner(algorithm, key);
    String base64UrlSignature = signer.sign(jwt);
    jwt += JwtParser.SEPARATOR_CHAR + base64UrlSignature;
} else {
    jwt += JwtParser.SEPARATOR_CHAR;
}
 | 
이제 마지막으로 서명부분을 만들게 되는데 sign는 앞에서 만들어진 헤더와 , 페이로드를 통해서 만들어지게 됩니다 이때 암호화 알고리즘을 사용해서 만들어지고 이를 마찬가지로 . 을 기준으로 BASE64로 인코딩을 해서 페이로드 다음으로 붙여서 반환하게 됩니다