본문 바로가기

보안

JWT 토큰

728x90
반응형

JWT란?

Json Web Token.

Json형태로 안전하게 정보를 전달하는 Open Standard이다.

JWT정보는 디지털 서명이 되어 있어서 안전하다.

JWT는 HMAC또는 RSA, ECDSA를 사용하는 공개키/개인키쌍을 서명할 수 있다.

 

JWT는 언제 쓸까?

Authorization인증은 JWT를 쓰는 가장 대표적인 이유다. 사용자가 로그인할 때 JWT 토큰을 함께 전달하면 

사용자가 해당 토큰으로 허용되는 경로나 리소스에 접근할 수 있다.

SSO는 널리 쓰이는 기능이다. 작은 오버헤드와 다른 도메인에서 쉽게 쓸 수 있기 때문이다.

 

Information ExchangeJson Web Token은 당사자들간에 정보를 전달하는 안전한 방법이다.

JWT는 공개키/개인키 쌍으로 서명해서 전달하기 때문에 발신자가 본인이 맞는지 확인할 수 있다. 또한 signature가 헤더와 payload를 사용해서 만들어지기 때문에 전달하는 내용이 변조되지 않았는지 확인할 수 있다.

 

 

JWT 구조

JWT는 세 가지 구조로 만들어지며 (.)를 통해 구분한다.

 

  • Header
  • Payload
  • Signature
xxxxx.yyyyy.zzzzz

이런식으로 만들어진다.

 

 

JWT Header

헤더는 일반적으로 두 가지 파트로 구성된다. 

토큰 타입과 알고리즘 (HMAC SHA256, RSA ..)

{
  "alg": "HS256",
  "typ": "JWT"
}

위의 JSON을 Base64URL encoded를 통해서 첫 번째 JWT값을 만든다. 

 

Payload

payload에는 사용자에 대한 정보나 추가 데이터에 대한 정보가 들어간다.

Registered claims, Public claims, Private claims

세 가지 정도가 들어갈 수 있다. 

{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}

JSON을 Base64URL Encoded하여 두 번째 JWT 값을 만든다.

 

 

Signature

인코딩된 헤더를 가져와서 서명을 만드는 부분이다.

인코딩된 payload, secret, 알고리즘을 가져와서 서명한다.

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)

서명은 메세지가 변경되지 않았는지 확인하는 용도로 사용되며 개인키로 서명된 경우 발신자를 확인할 수 있다. 

 

jwt.io/#debugger-io

 

JWT.IO

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

jwt.io

위 사이트에서 JWT 토큰을 디코딩해볼 수 있다. 좋다.


JWT Token Teset

import io.jsonwebtoken.Header;
import io.jsonwebtoken.JwsHeader;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

import javax.crypto.spec.SecretKeySpec;
import java.security.Key;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

public class JWTTokenTest {

    public static void main(String[] args) {
        String token = JWTTokenTest.createToken();
        System.out.printf("token : "  + token);
    }

    private static String createToken() {
        Map<String, Object> headers = new HashMap<>();
        Map<String, Object> body = new HashMap<>();

        String token = null;

        headers.put(Header.TYPE , Header.JWT_TYPE);
        headers.put(JwsHeader.ALGORITHM, SignatureAlgorithm.HS256);

        body.put("DATA", "Test");
        Long expiredTime = 1000 * 60L * 60L * 2L; // 토큰 유효 시간 (2시간)

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

        String signKey = UUID.randomUUID().toString();

        Key key = new SecretKeySpec(signKey.getBytes(), SignatureAlgorithm.HS256.getJcaName());
        token = Jwts.builder().setHeader(headers).setClaims(body)
                .setExpiration(ext).signWith(key, SignatureAlgorithm.HS256)
                .compact();
        return token;
    }


}

header에는 JWT_TYPE과 알고리즘 HS256만 추가하고(기본설정)

body에는 key : Data, value : Test만 넣었다.

만료시간은 2시간으로 설정했다. 

token : eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJEQVRBIjoiVGVzdCIsImV4cCI6MTYxMDE3NDc0OH0.OYN5MXM8yK4zgTo6qZHVoBIKLIHugdLtREra_ZMWLuY

발급받은 JWT Token이다.

 

jwt.io에서 디버깅 해봤다.

payload에서 데이터가 들어간걸 확인할 수 있다.

 

Token 검증 

public class JWTTokenTest {

    public static String signKey = "b93fb8f2-8cc1-4d67-85c5-f470091115e1";
    public static void main(String[] args) {
        String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJEQVRBIjoiVGVzdCIsImV4cCI6MTYxMDE3NjUwOH0.Rwsv-dtJUt8-tiY-X5GxEXeS7RLcxCBRPeg7hIFbL9I";
        boolean result = JWTTokenTest.parsingToken(token);
        System.out.println("token result  : " + result);
    }

    private static boolean parsingToken(String token) {
        Key key = new SecretKeySpec(signKey.getBytes(), SignatureAlgorithm.HS256.getJcaName());
        JwtParser parser = Jwts.parser().setSigningKey(key);
        Jws<Claims> claimsJwts = parser.parseClaimsJws(token);
        Map<String, Object> body = claimsJwts.getBody();

        for(String k : body.keySet()) {
            System.out.println("key : " + key + ",  body : " + body.get(k));
        }
        Claims claims = claimsJwts.getBody();
        Date date = claims.getExpiration();
        System.out.println(" date : " + date);
        Date now = new Date();
        System.out.println("now : " + now);
        return now.before(date);
    }
}

 

실무에서 쓸때는 Token이 header에 Autorization으로 들어올거니까 

알아서 파싱해서 쓰면 됨.

 

728x90
반응형

'보안' 카테고리의 다른 글

비밀번호 해시 솔트  (0) 2020.06.29