728x90

 

일단 build.gradle 에 의존성을 추가하자 

참고로 jdk 17 을 사용 했다

1
2
3
4
5
// JWT
// https://github.com/jwtk/jjwt
implementation 'io.jsonwebtoken:jjwt-api:0.12.5'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.5'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.5'
cs

 

 

다음은 JwtTest 파일 이다 

1
2
3
4
5
6
7
8
9
10
class JwtTest {
 
 
    String secretKey = "66650E3CBCF3E6E1DB2F5D3CDCAB8B9AED0198DE0C060064E270C2199329F1B85191162AA80AD0C4EC79A38E2E475186E9E7F0BBAD38D05A9C732F5B5B5E7B2C";
 
    byte[] secretKeyBytes = secretKey.getBytes(StandardCharsets.UTF_8);
    SecretKey key = Keys.hmacShaKeyFor(secretKeyBytes);// 512 비트이면 SHA-512 동작한다
    
     ...   
}
cs

 

 

hmacShaKeyFor 메서드를 보면 512 비트가 넘어가면 sha-512 해시함수를 사용한다 

그러기 때문에 시크릿키가 512 비트가 넘어가는 문자열을 준비했다

 

 

 

토큰 생성 테스트 코드이다 보면 쉽다

claim 부분에 내가 토큰에 넣을 데이터를 넣어주는데

테스트여서 패스워드 칼럼을 넣었지만, 실무에서는 개인정보는 넣지말자

 

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
@Test
void 토큰_생성() {
 
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:sss");
 
    // 현재 순간
    Instant instant = Instant.now();
 
 
    // 생성일
    Date createDate = Date.from(instant);
    String startDateText = sdf.format(createDate);
 
    // 만료일
    Date endDate = Date.from(instant.plus(30, ChronoUnit.SECONDS));    // 30초 후
    String endDateText = sdf.format(endDate);
 
 
    System.out.println("startDateText = " + startDateText);
    System.out.println("endDateText = " + endDateText);
 
 
    String token = Jwts.builder()
            .subject("토큰제목")
            .issuedAt(createDate)
            .expiration(endDate)
            .claim("userId""1111")
            .claim("password""2222")
            .signWith(key)  // 서명
            .compact();
 
 
    System.out.println("token");
    System.out.println(token);
 
 
}
 
cs

 

결과는 다음과 같다

 

1
2
3
4
5
startDateText = 2024-06-03 00:00:049
endDateText = 2024-06-03 00:01:019
token
eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiLthqDtgbDsoJzrqqkiLCJpYXQiOjE3MTczNDA0NDksImV4cCI6MTcxNzM0MDQ3OSwidXNlcklkIjoiMTExMSIsInBhc3N3b3JkIjoiMjIyMiJ9.EFqJ9KI2rVVqrcpxgeFqzoGv_TVOUFgKWBcZ-LaYuk-a_Z6HwL05e4t07V_b_VB-vrWJxw_9WrXlHN5k5VPFaw
 
cs

 

 

이제 검증을 해보자

 

 

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
@Test
void 토큰_검증() throws Exception {
 
    JwtParser jwtParser = Jwts.parser()
            .verifyWith(key)
            .build();
 
    try {
 
        // 파라미터로 토큰 문자열을 넣어준다
        // 토큰이 만료되면 ExpiredJwtException 예외가 터진다
        Jwt<?, ?> jwt = jwtParser.parse("eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiLthqDtgbDsoJzrqqkiLCJpYXQiOjE3MTcyNDc3ODQsImV4cCI6MTcxNzI0NzgxNCwidXNlcklkIjoiMTExMSIsInBhc3N3b3JkIjoiMjIyMiJ9.mNz4hwmszUMd1XNGX9_7UK1ND5h2e5cIsJpXnln2Tyb4SZ9diHRtUzJyKw_xstd4vxGIcwBSwt4mWxu9qxDJAQ");
        System.out.println("jwt = " + jwt);
 
        Header header = jwt.getHeader();
        System.out.println("header = " + header);
 
 
        String alg = (String) header.get("alg");
        System.out.println("alg = " + alg);
 
 
        Claims claims = (Claims) jwt.getPayload();
        System.out.println("claims = " + claims);
 
        String userId = (String) claims.get("userId");
        String password = (String) claims.get("password");
 
        System.out.println("userId = " + userId);
        System.out.println("password = " + password);
 
    } catch(Exception e) {
        // ExpiredJwtException
        e.printStackTrace();
    }
 
 
}
 
cs

 

 

 

토큰이 만료되었다면 다음과 같은 ExpiredJwtException 예외가 터진다

 

 

 

토큰이 유효하면 다음과 같이 출력이 잘된다

 

1
2
3
4
5
6
7
jwt = header={alg=HS512},payload={sub=토큰제목, iat=1717248046, exp=1717248076, userId=1111, password=2222},signature=OSmw7n8OF_crspRpAWhKJ_jEvWrUfsCVxCIcx_supn4ggHDewyJN0132V6vg__4UWMM0hcbBthXTZKExQZ5FRA
header = {alg=HS512}
alg = HS512
claims = {sub=토큰제목, iat=1717248046, exp=1717248076, userId=1111, password=2222}
userId = 1111
password = 2222
 
cs

 

 

 

다음은 토큰을 그냥 Base64 로 디코딩 해서 읽어버리자

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Test
public void base64_decoding() throws Exception {
 
    ObjectMapper mapper = new ObjectMapper();
 
 
    String token = "eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiLthqDtgbDsoJzrqqkiLCJpYXQiOjE3MTcyNDgwNDYsImV4cCI6MTcxNzI0ODA3NiwidXNlcklkIjoiMTExMSIsInBhc3N3b3JkIjoiMjIyMiJ9.OSmw7n8OF_crspRpAWhKJ_jEvWrUfsCVxCIcx_supn4ggHDewyJN0132V6vg__4UWMM0hcbBthXTZKExQZ5FRA";
    String[] chunks = token.split("\\.");
    Base64.Decoder decode = Base64.getUrlDecoder();
    byte[] decodeByteArr = decode.decode(chunks[1]);    // 0 : 헤더, 1 : 페이로드, 2 : 시그니처
    String jsonText = new String(decodeByteArr);
 
    Map<String,String> dataMap = mapper.readValue(jsonText, Map.class);
 
    System.out.println("dataMap = " + dataMap);
 
}
 
cs

 

 

출력은 다음과 같다

 

 

1
dataMap = {sub=토큰제목, iat=1717248046, exp=1717248076, userId=1111, password=2222}
cs

 

 

시큐리티에 녹이는건 너무 분량이많아서 생략한다...

 

 

728x90

+ Recent posts