본문 바로가기
Java 웹 프로그래밍

JWT (JSON Web Token) - 3

by irerin07 2020. 4. 28.
728x90

JWT (JSON Web Token) - 1

JWT (JSON Web Token) - 2

JWT (JSON Web Token) - 3

JWT, JSON Web Token은 인증에 필요한 정보들을 암호화 한 토큰이다.

사용자는 이 토큰을 HTTP헤더에 담아서 서버로 보내게 된다.

HS512를 사용한 JWT

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

https://www.toptal.com/web/cookie-free-authentication-with-json-web-tokens-an-example-in-laravel-and-angularjs

https://velopert.com/2389

728x90