1. JWT(Json Web Token)란?

JWT는 개체와 개체 간에 정보 전달권한 인가(Authorization)를 위해 사용되는 토큰입니다.
URL-safety하기 때문에 Header 또는 URL을 통해 전달합니다.


2. JWT 구조

JWT는 Header, Payload, Signature로 이루어져있습니다.
각 부분은 Json형태로 저장되어 있으며, 각각 Base64로 인코딩 되어 있고, "." 을 구분자로 각각 부분을 구분합니다.

Header : 토큰에 대한 기본정보
Payload : 전달할 정보
signature : 검증된 토큰 정보 

 

1) HEADER(헤더)

토큰에 대한 기본 정보가 저장됩니다.

alg : Signature를 어떤 알고리즘으로 암호화 했는지 명시한다. (ex : SHA256, RSA)
typ : 토큰의 타입을 지정한다. (ex : jwt)


2) PAYLOAD(페이로드)

전달하려고 하는 정보가 저장되어있습니다. PAYLOAD는 크게 3가지로 구분됩니다. 

등록된 클레임(Registered Claim)
이름이 이미 정해진 클레임입니다. 등록된 클레임은 모두 선택적으로 사용할 수 있습니다.

iss(issuer) : 토큰 발급자
sub(subject) : 토큰 제목
aud(audience) : 토큰 대상자
exp(expiration) : 토큰 만료 시간, NumericDate 형식으로 저장된다. (ex : 1480849147370)
nbf(not before) : 토큰 활성 날짜, 이 날이 지나기전에 토큰이 활성화되지 않는다.
iat(isuued at) : 토큰 발급 시간, 토큰 발급 이후의 경과시간을 알 수 있다.
jti : JWT의 고유 식별자, 중복적인 처리를 방지하기 위해서 사용한다. 

공개 클레임(Public Claim)
사용자 정의 클레임으로, 공개용 정보를 위해 사용됩니다.

비공개 클레임(Private Claim)
서버와 클라이언트 간 협의하에 사용되는 클레임입니다. 

3) Signature(서명)

토큰의 유효성을 검증할 때 사용하는 서명입니다. 서명은 헤더와 페이로드의 값을 각각 BASE64로 인코딩하고 "."를 구분자로 붙여줍니다. 그리고 비밀키를 이용해 헤더에서 정의한 알고리즘으로 암호화한 후, 다시 BASE64로 인코딩합니다.

//sha256으로 암호화 할 경우

header = base64_encode(header_json)
payload = base64_encode(payload_json);
signature = base64_encode(HMAC_SHA256(header + "." + payload, secret))
jwt = header + "." + payload + "." + signature


3. JWT의 용도 

회원 인증 
회원이 로그인을 하면 서버는 회원정보가 담긴 토큰을 발급해 줍니다. 그리고 회원이 서버에 데이터를 요청 할 때 마다 발급 받은 토큰 정보를 함께 전달하여, 인증받은 회원이라는 것을 증명합니다. 

정보 전달 
JWT는 Signature 정보를 이용해서 데이터 조작을 방지할 수 있으며, 안전하게 정보를 전달할 수 있습니다.


4. JWT의 장단점

장점

1) Stateless이며 확장성이 있다. 
서버와 클라이언트 간에 상태 정보를 저장하지 않으며 토큰을 가지고 있고 유효하다면 서버로 부터 데이터를 요청할 수 있습니다. 즉 개체간에 연결을 유지하지 않아도 됩니다.

서버를 여러대 사용하거나, 클라이언트 단말의 종류(PC, 모바일 등..)가 다양할 경우에도 토큰만 유효하다면 어떤 단말이든지간에 서버로 부터 데이터를 받을 수 있습니다. 
즉 JWT 인증 시스템을 만들어 두면 플랫폼에 상관없이 회원 인증 시스템 및 정보 전달이 가능합니다. 

2) 사용자 인증에 필요한 인증 저장소가 필요없다.
인증 정보가 토큰 자체에 포함되어있기 때문에 별도의 인증 저장소가 필요없습니다.

단점

1) 한번 발급된 토큰은 수정 및 삭제할 수 없다.
JWT는 stateless하기 때문에 토큰에 대한 정보를 관리하지 않습니다. 그래서 한번 만들어진 토큰은 삭제, 수정할 수 없습니다. 만약 토큰이 유출된다면 토큰이 만료될 때 까지 토큰을 가로챈 사용자가 서비스를 이용할 수 있습니다.

2) Payload 인코딩
Payload 값이 암호화된 값이 아닌 Base64로 인코딩 된 값이라 누구나 확인할 수 있습니다.
그래서 중요한 정보를 Payload에 저장해서는 안됩니다.

 

참조

https://mangkyu.tistory.com/56

https://jwt.io/

'IT > 기타' 카테고리의 다른 글

[Javascript] Jqgrid Custom  (0) 2020.09.14
[DEV] 컴파일과 빌드란  (0) 2020.09.10
[보안] 공개키 암호화 방식  (0) 2020.07.20
XSS란?  (0) 2020.07.16
OAuth2 란?  (1) 2020.02.29

JWT란?

2020/10/02 - [IT/기타] - [JWT] Json Web Token이란?

 

1. jjwt란?

jjwt는 JWT 토큰 생성JWT 토큰 파싱, 검증을 해주는 라이브러리 입니다.

 

2. Maven dependency 추가

...
    <dependencies>
        ...
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>
        ...
    </dependencies>
...

 

3. 예제

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.Jwts;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class TestJWT {
    final String key = "Bamdule";

    public static void main(String[] args) throws UnsupportedEncodingException {
        TestJWT testJWT = new TestJWT();

        String jwt = testJWT.createToken();
        System.out.println(jwt);
        
        Map<String, Object> claimMap = testJWT.verifyJWT(jwt);
        System.out.println(claimMap); // 토큰이 만료되었거나 문제가있으면 null
    }
        
    //토큰 생성
    public String 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("data", "My First JWT !!");

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

        Date ext = new Date(); // 토큰 만료 시간
        ext.setTime(ext.getTime() + expiredTime);
     
        // 토큰 Builder
        String jwt = Jwts.builder()
                .setHeader(headers) // Headers 설정
                .setClaims(payloads) // Claims 설정
                .setSubject("user") // 토큰 용도 
                .setExpiration(ext) // 토큰 만료 시간 설정
                .signWith(SignatureAlgorithm.HS256, key.getBytes()) // HS256과 Key로 Sign
                .compact(); // 토큰 생성

        return jwt;
    }
    
    //토큰 검증
    public Map<String, Object> verifyJWT(String jwt) throws UnsupportedEncodingException {
        Map<String, Object> claimMap = null;
        try {
            Claims claims = Jwts.parser()
                    .setSigningKey(key.getBytes("UTF-8")) // Set Key
                    .parseClaimsJws(jwt) // 파싱 및 검증, 실패 시 에러
                    .getBody();

            claimMap = claims;

            //Date expiration = claims.get("exp", Date.class);
            //String data = claims.get("data", String.class);
            
        } catch (ExpiredJwtException e) { // 토큰이 만료되었을 경우
            System.out.println(e);
            ...
        } catch (Exception e) { // 그외 에러났을 경우
            System.out.println(e);
            ...
        }
        return claimMap;
    }    
}

 

4. 결과

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c2VyIiwiZGF0YSI6Ik15IEZpcnN0IEpXVCAhISIsImV4cCI6MTU5NzIxNDQzM30.a51SU-jC5OgvX2JgiNShQziKDcO_ARLGBpsnbwbBxqs


{sub=user, data=My First JWT !!, exp=1597145971}

 

1) 토큰 만료 시 출력되는 메시지

io.jsonwebtoken.ExpiredJwtException: JWT expired at 2020-08-12T15:40:33Z. Current time: 2020-08-12T15:40:34Z, a difference of 1388 milliseconds. Allowed clock skew: 0 milliseconds.

 

+ Recent posts