개발 지식/Spring Framework

[JWT] Spring Boot 환경에서 JWT(Json Web Token)생성 하기

에르주 2021. 10. 26. 00:27
반응형

첫번째의 JWT는 JWT에 대한 간단한 설명을 정리했고 2번째는 Spring Boot 환경에서 JWT를 직접 생성해보고자 한다.

사실 JWT 생성은
https://jwt.io/

 

JWT.IO

JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties.

jwt.io


해당 홈페이지에서 직접 PayLoad값을 넣어가며 생성할 수 있다.
하지만 실무 백엔드에서 JWT를 활용할 수 있는 간단한 JWT 생성코드를 정리해 보고자한다. 생성에는 jjwt 플러그인을 활용하였다.


1. build.gradle 설정에 io.jsonwebtoken:jjwt 플러그인을 추가한다.

implementation 'io.jsonwebtoken:jjwt-api:0.11.2' 
implementation 'io.jsonwebtoken:jjwt-impl:0.11.2' 
implementation 'io.jsonwebtoken:jjwt-jackson:0.11.2'

 

2. JWT 코드 작성하기

우선 JWT 생성은 간단하게 4가지 단계가 있다.

  1. Header 값 생성
  2. PayLoad
  3. Signature(서명)
  4. 생성

1,2번의 값들은 모두 Map형태의 Key, Value 값으로 정의한다.

2-1. Header값 생성

Map<String, Object> headers = new HashMap<>(); headers.put("typ", "JWT"); // 타입은 JWT headers.put("alg", "HS256"); // 서명 알고리즘 유형

 

2-2. PayLoad값 생성

Map<String, Object> payloads = new HashMap<>(); 
payloads.put("KEY", "HelloWorld"); payloads.put("NickName","Erjuer"); 
payloads.put("Age","29"); payloads.put("TempAuth","Y");

 

2-3. Signature(서명)

JWT 관련하여 구글링을 해보니 서명 부분에서 .signWith(io.jsonwebtoken.SignatureAlgorithm, java.lang.String)를 활용하는 예제들을 많이 봤다.
하지만 signWith(io.jsonwebtoken.SignatureAlgorithm, java.lang.String)' is deprecated 되어 String값을 넣는 것이 아닌 Key값을 생성하고 서명을 진행해야 한다.

// String str = "MyNickNameisErjuerAndNameisMinsu" 값을 byte 형변환 
Key key = Keys.hmacShaKeyFor("MyNickNameisErjuerAndNameisMinsu".getBytes(StandardCharsets.UTF_8));


String 값으로 Utf8로 인코딩후 Byte로 형변환 할 때 다음과 같은 Exception이 터질 수도 있다.

io.jsonwebtoken.security.WeakKeyException: The specified key byte array is 96 bits which is not secure enough for any JWT HMAC-HA algorithm.
The JWT JWA Specification (RFC 7518, Section 3.2) states that keys used with HMAC-SHA algorithms MUST have a size >= 256 bits (the key size must be greater than or equal to the hash output size).
Consider using the io.jsonwebtoken.security.Keys
#secretKeyFor(SignatureAlgorithm) method to create a key guaranteed to be secure enough for your preferred HMAC-SHA algorithm.
See https://tools.ietf.org/html/rfc7518#section-3.2 for more information.

: 요약하자면 256bit보다 커야 한다. 영어 한단어당 8bit 이므로 32글자 이상이어야 한다는 뜻이다.

여기서 궁금한 것 하나 한글은 한글자당 16bit인데 16글자이면 생성이 될까?
'제블로그에오신여러분을환영합니다' => 생성된다.

 

 public static SecretKey hmacShaKeyFor(byte[] bytes) throws WeakKeyException {
        if (bytes == null) {
            throw new InvalidKeyException("SecretKey byte array cannot be null.");
        }
        int bitLength = bytes.length * 8;
        for (SignatureAlgorithm alg : PREFERRED_HMAC_ALGS) {
            if (bitLength >= alg.getMinKeyLength()) {
                return new SecretKeySpec(bytes, alg.getJcaName());
            }
        }

        String msg = "The specified key byte array is " + bitLength + " bits which " +
                "is not secure enough for any JWT HMAC-SHA algorithm.
        The JWT " + " JWA Specification (RFC 7518, Section 3.2)
        states that keys used with HMAC -SHA algorithms MUST have a "
                + "size >= 256 bits (the key size must be greater than or equal to the hash "
                + "output size). Consider using the " + Keys.class.getName()
                + "#secretKeyFor(SignatureAlgorithm) method "
                + "to create a key guaranteed to be secure enough for your preferred HMAC-SHA algorithm. See " + "https://tools.ietf.org/html/rfc7518#section-3.2 for more information.";
        throw new WeakKeyException(msg);
    }

 

2-4. 생성

토큰 만료 시간을 설정해주고 Header값과 payLoad 그리고 서명을 진행한다.

 // 토큰 만료 시간 Long expiredTime = 1000 * 60L * 60L * 1L; 
 // 토큰 유효 시간 (밀리 세컨드 단위) 
 Date expireDate = new Date(); 
 expireDate.setTime(expireDate.getTime() + expiredTime); 
 // 토큰 Builder String jwt = Jwts.builder() .setHeader(headers) 
 // Headers 설정 .setClaims(payloads) 
 // Claims 설정 .setSubject("Test") 
 // 토큰 용도 .setExpiration(expireDate) 
 // 토큰 만료 시간 설정 
 .signWith(key, SignatureAlgorithm.HS256) .compact(); // 토큰 생성

토큰 값 생성

JWT : eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJOaWNrTmFtZSI6IkVyanVlciIsIlRlbXBBdXRoIjoiWSIsIktFWSI6IkhlbGxvV29ybGQiLCJBZ2UiOiIyOSIsInN1YiI6IlRlc3QiLCJleHAiOjE2MzUxNzg4NTF9.ad539kso90a8Hm5_Iv3xiXgrLJ4Qjo52sR7pXGr4Hr0

JWT값이 생성되는 것을 확인할 수 있다.


JWT값을 jwt.io 페이지에서 확인해보면

JWT 파싱

PayLoad값에 내가 Map 형태로 저장했던 값들을 확인할 수 있다.


해당 값을 Fronted와 Backend HTTP 통신에 Header값으로 넣고 설정하여 간단한 인증 절차를 진행 할 수 있다.
다음 정리글은 받은 값을 Header와 PayLoad로 추출해보는 작업을 진행해보려 한다.


<JWT 생성 예시 코드>

@SpringBootTest
public class JwtTest {

    @Test
    public void createToken() {

        //Header 부분 설정
        Map<String, Object> headers = new HashMap<>();
        headers.put("typ", "JWT");
        headers.put("alg", "HS256");

        //payload 부분 설정
        Map<String, Object> payloads = new HashMap<>();
        payloads.put("KEY", "HelloWorld");
        payloads.put("NickName","Erjuer");
        payloads.put("Age","29");
        payloads.put("TempAuth","Y");

        Long expiredTime = 1000 * 60L * 60L * 1L; // 토큰 유효 시간 (2시간)

       Date date = new Date(); // 토큰 만료 시간
        date.setTime(date.getTime() + expiredTime);

        Key key = Keys.hmacShaKeyFor("MyNickNameisErjuerAndNameisMinsu".getBytes(StandardCharsets.UTF_8));

        // 토큰 Builder
        String jwt = Jwts.builder()
                .setHeader(headers) // Headers 설정
                .setClaims(payloads) // Claims 설정
                .setSubject("Test") // 토큰 용도
                .setExpiration(date) // 토큰 만료 시간 설정
                .signWith(key, SignatureAlgorithm.HS256)
                .compact(); // 토큰 생성


        System.out.println(">> jwt : " + jwt);
    }
}


끝.

반응형