JWT, JSON Web Token은 인증에 필요한 정보들을 암호화 한 토큰이다.
사용자는 이 토큰을 HTTP헤더에 담아서 서버로 보내게 된다.
JWT는 크게 3부분으로 나뉜다.
[Base64(HEADER)].[Base64(PAYLOAD)].[Base64(SIGNATURE)]
각 부분들은 모두 Base64를 통해 인코딩 된다.
1. HEADER
헤더는 두가지 정보를 지니고 있다.
alg: 해싱 알고리즘 (HS512). 이 알고리즘은 토큰 검증시 사용되는 verify signature에서 사용된다.
typ: 토큰의 타입 (JWT)
{
"alg": "HS256",
"typ": "JWT"
}
이 값을 Base64로 인코딩을 하면
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
위와 같은 값이 나온다.
2. PAYLOAD
페이로드에는 토큰에 담을 정보가 들어있다.
페이로드에 담긴 정보의 한 조각을 'claim'이라 하고 이는 name과 value 한 쌍으로 이루어져 있다.
claim은 크게 세 분류로 나뉜다.
registered(등록된) claim
public(공개) claim
private(비공개) claim
2-1. registered claim
서비스에서 필요한 정보들은 아니지만 토큰에 대한 정보들을 담기 위해 이름이 이미 정해진(등록된) 클레임이다.
이들의 사용여부는 모두 필수가 아닌 선택적이다.
- iss: 토큰 발급자 (issuer)
- sub: 토큰 제목 (subject)
- aud: 토큰 대상자 (audience)
- exp: 토큰의 만료시간 (expiraton), 시간은 NumericDate 형식으로 되어있어야 하며 (예: 1480849147370) 언제나 현재 시간보다 이후로 설정되어있어야한다.
- nbf: Not Before 를 의미하며, 토큰의 활성 날짜와 비슷한 개념이다. 여기에도 NumericDate 형식으로 날짜를 지정하며, 이 날짜가 지나기 전까지는 토큰이 처리되지 않는다.
- iat: 토큰이 발급된 시간 (issued at), 이 값을 사용하여 토큰의age가 얼마나 되었는지 판단 할 수 있다.
- jti: JWT의 고유 식별자로서, 주로 중복적인 처리를 방지하기 위하여 사용됩니다. 일회용 토큰에 사용하면 유용하다.
2-2. public claim
사용자에 의해 정의되는 커스텀 claim 이름으로서 충돌을 방지하기 위해 URI형식으로 이름을 짓는다.
{
"https://irerin.tistory.com/jwt_claims/is_admin": true
}
2-3. private claim
클라이언트와 서버, 양측간 협의하에 사용되는 claim.
public claim과 달리 이름이 중복되어 충돌할 수 있으니 주의해야한다.
{
"username": "irerin"
}
PAYLOAD 예시
{
"iss": "irerin07.tistory.com",
"exp": "1485270000000",
"https://irerin07.tistory.com/jwt_claims/is_admin": true,
"userId": "11028373727102",
"username": "irerin"
}
위의 예시를 base64로 인코딩하면 다음과 같은 결과가 나온다
eyJpc3MiOiJpcmVyaW4wNy50aXN0b3J5LmNvbSIsImV4cCI6IjE0ODUyNzAwMDAwMDAiLCJodHRwczovL2lyZXJpbjA3LnRpc3RvcnkuY29tL2p3dF9jbGFpbXMvaXNfYWRtaW4iOnRydWUsInVzZXJJZCI6IjExMDI4MzczNzI3MTAyIiwidXNlcm5hbWUiOiJpcmVyaW4ifQ
"주의:base64로 인코딩을 할 때dA== 처럼 뒤에= 문자가 한두개 붙을 때가 있습니다. 이 문자는base64인코딩의padding 문자라고 부릅니다.
JWT 토큰은 가끔 URL 의 파라미터로 전달 될 때도 있는데요, 이= 문자는,url-safe하지 않으므로, 제거되어야 합니다. 패딩이 한개 생길 때도 있고, 두개 생길 때도 있는데,전부 지워주어야합니다.(제거해줘도 디코딩 할 때 전혀 문제가 되지 않습니다)"
3. SIGNATURE
JWT의 마지막 부분으로 헤더의 인코딩값과 페이로드의 인코딩값을 합친 후 주어진 비밀키로 해쉬를 하여 생성한다.
HMACSHA512(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
your-256-bit-secret
)
"문자열을 인코딩 하는게 아닌hex→base64인코딩을 해야합니다"
위에서 생성한 헤더와 페이로드를 '.'를 사용해 합친 후
eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJpcmVyaW4wNy50aXN0b3J5LmNvbSIsImV4cCI6IjE0ODUyNzAwMDAwMDAiLCJodHRwczovL2lyZXJpbjA3LnRpc3RvcnkuY29tL2p3dF9jbGFpbXMvaXNfYWRtaW4iOnRydWUsInVzZXJJZCI6IjExMDI4MzczNzI3MTAyIiwidXNlcm5hbWUiOiJpcmVyaW4ifQ
해당 값을 secret key로 (여기서는 secret을 사용)해싱을 한 뒤 base 64로 인코딩을 하면 다음과 같은 값이 나온다.
52_5IEogXkflP8FR-PhYgMHp8RIbp-HLh7R9GOMjgXAYCNsntj3qqhGUKvqwmqlzaHOz7K8oOm0ahobCo9-ZeA
이 부분 역시 padding이 생기면 지워준다.
이제 모든 값들을 합치면
eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJpcmVyaW4wNy50aXN0b3J5LmNvbSIsImV4cCI6IjE0ODUyNzAwMDAwMDAiLCJodHRwczovL2lyZXJpbjA3LnRpc3RvcnkuY29tL2p3dF9jbGFpbXMvaXNfYWRtaW4iOnRydWUsInVzZXJJZCI6IjExMDI4MzczNzI3MTAyIiwidXNlcm5hbWUiOiJpcmVyaW4ifQ.52_5IEogXkflP8FR-PhYgMHp8RIbp-HLh7R9GOMjgXAYCNsntj3qqhGUKvqwmqlzaHOz7K8oOm0ahobCo9-ZeA
위와 같은 하나의 토큰이 만들어진다.
위의 토큰을 https://jwt.io/ 사이트의 디버거에 등록하면
성공적으로 JWT토큰이 검증되는것을 확인할 수 있다. (검증 성공지 Signature Verified라고 문구가 나온다.)
참고자료:
JSON Web Token (JWT)
http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html
JWT – Claims and Signing
http://self-issued.info/docs/draft-jones-json-web-token-01.html
JSON Web Token Tutorial: An Example in Laravel and AngularJS
'Java 웹 프로그래밍' 카테고리의 다른 글
equals()와 hashCode() (0) | 2020.06.16 |
---|---|
오늘의 뻘짓 (0) | 2020.06.02 |
JWT (JSON Web Token) - 2 (0) | 2020.04.28 |
JWT (JSON Web Token) - 1 (0) | 2020.04.28 |
자바 스트링, 스트링빌더, 스트링버퍼 Java String, StringBuilder, StringBuffer (0) | 2020.04.03 |