JWT란?
Json Web Token.
Json형태로 안전하게 정보를 전달하는 Open Standard이다.
JWT정보는 디지털 서명이 되어 있어서 안전하다.
JWT는 HMAC또는 RSA, ECDSA를 사용하는 공개키/개인키쌍을 서명할 수 있다.
JWT는 언제 쓸까?
Authorization : 인증은 JWT를 쓰는 가장 대표적인 이유다. 사용자가 로그인할 때 JWT 토큰을 함께 전달하면
사용자가 해당 토큰으로 허용되는 경로나 리소스에 접근할 수 있다.
SSO는 널리 쓰이는 기능이다. 작은 오버헤드와 다른 도메인에서 쉽게 쓸 수 있기 때문이다.
Information Exchange : Json 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 토큰을 디코딩해볼 수 있다. 좋다.
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으로 들어올거니까
알아서 파싱해서 쓰면 됨.
'보안' 카테고리의 다른 글
비밀번호 해시 솔트 (0) | 2020.06.29 |
---|