본문 바로가기
개발 지식/Spring Framework

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

by 에르주 2021. 10. 26.
반응형

첫번째의 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);
    }
}


끝.

반응형

댓글