JWT - JSON Web Token

ํ† ํฐ ๊ธฐ๋ฐ˜ ์ธ์ฆ

https://velopert.com/wp-content/uploads/2016/12/token-diagram.png

ํ† ํฐ ๊ธฐ๋ฐ˜ ์‹œ์Šคํ…œ์€ statelessํ•˜๋ฏ€๋กœ ์œ ์ €์˜ ์ธ์ฆ ์ •๋ณด๋ฅผ ์„œ๋ฒ„๋‚˜ ์„ธ์…˜์— ๋‹ด์•„๋‘์ง€ ์•Š๋Š”๋‹ค.

  1. ์œ ์ €๊ฐ€ ์•„๋””์ด์™€ ๋น„๋ฐ€๋ฒˆํ˜ธ๋กœ ๋กœ๊ทธ์ธ์„ ํ•œ๋‹ค.

  2. ์„œ๋ฒ„์ธก์—์„œ ํ•ด๋‹น ๊ณ„์ •์ •๋ณด๋ฅผ ๊ฒ€์ฆํ•œ๋‹ค.

  3. ๊ณ„์ •์ •๋ณด๊ฐ€ ์ •ํ™•ํ•˜๋ฉด, ์„œ๋ฒ„์ธก์—์„œ ์œ ์ €์—๊ฒŒ signed ํ† ํฐ์„ ๋ฐœ๊ธ‰ํ•ด์ค€๋‹ค.

    signed๋Š” ํ•ด๋‹น ํ† ํฐ์ด ์„œ๋ฒ„์—์„œ ์ •์ƒ์ ์œผ๋กœ ๋ฐœ๊ธ‰๋œ ํ† ํฐ์ž„์„ ์ฆ๋ช…ํ•˜๋Š” signature์ด๋‹ค.

  4. ํด๋ผ์ด์–ธํŠธ ์ธก์—์„œ ์ „๋‹ฌ๋ฐ›์€ ํ† ํฐ์„ ์ €์žฅํ•ด๋‘๊ณ , ์„œ๋ฒ„์— ์š”์ฒญํ•  ๋•Œ๋งˆ๋‹ค, ํ•ด๋‹น ํ† ํฐ์„ ํ•จ๊ป˜ ์„œ๋ฒ„์— ์ „๋‹ฌํ•œ๋‹ค.

  5. ์„œ๋ฒ„๋Š” ํ† ํฐ์„ ๊ฒ€์ฆํ•˜๊ณ  ์š”์ฒญ์— ์‘๋‹ตํ•œ๋‹ค.

์›น ์„œ๋ฒ„์—์„œ ํ† ํฐ์„ ์„œ๋ฒ„์— ์ „๋‹ฌํ•  ๋•Œ๋Š”, HTTP ์š”์ฒญ์˜ ํ—ค๋”์— ํ† ํฐ๊ฐ’์„ ํฌํ•จ์‹œ์ผœ์„œ ์ „๋‹ฌํ•œ๋‹ค.

์žฅ์ 

๋ฌด์ƒํƒœ(Stateless)์ด๋ฉฐ ํ™•์žฅ์„ฑ(scalability)๊ฐ€ ์žˆ๋‹ค.

ํ† ํฐ์€ ํด๋ผ์ด์–ธํŠธ ์‚ฌ์ด๋“œ์— ์ €์žฅํ•˜๊ธฐ๋•Œ๋ฌธ์— ์™„์ „ statelessํ•˜๋ฉฐ, ์„œ๋ฒ„ ํ™•์žฅํ•˜๊ธฐ์— ์ ํ•ฉํ•œ ํ™˜๊ฒฝ์„ ์ œ๊ณตํ•œ๋‹ค.

๋ณด์•ˆ์„ฑ

ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์„œ๋ฒ„์— ์š”์ฒญ์„ ๋ณด๋‚ผ ๋•Œ ์ฟ ํ‚ค๋ฅผ ์‚ฌ์šฉํ•จ์œผ๋กœ ์ธํ•ด ๋ฐœ์ƒํ•˜๋Š” ์ทจ์•ฝ์ ์ด ์‚ฌ๋ผ์ง„๋‹ค.

ํ™•์žฅ์„ฑ(Extensibility)

ํ† ํฐ์„ ์‚ฌ์šฉํ•ด ๋‹ค๋ฅธ ์„œ๋น„์Šค์—์„œ๋„ ๊ถŒํ•œ์„ ๊ณต์œ ํ•  ์ˆ˜ ์žˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด์„œ Fackbook, Naver, Google, Kakao ๊ณ„์ •์œผ๋กœ ๋กœ๊ทธ์ธํ•  ์ˆ˜ ์žˆ๋‹ค๋ฉด, ํ† ํฐ์— ์„ ํƒ์ ์ธ ๊ถŒํ•œ๋งŒ ๋ถ€์—ฌํ•ด ๋ฐœ๊ธ‰ํ•  ์ˆ˜ ์žˆ๋‹ค.

์—ฌ๋Ÿฌ ํ”Œ๋žซํผ ๋ฐ ๋„๋ฉ”์ธ

์„œ๋น„์Šค์˜ ๊ทœ๋ชจ๊ฐ€ ์ปค์ง€๋ฉด, ์šฐ๋ฆฌ๋Š” ์—ฌ๋Ÿฌ ๋””๋ฐ”์ด์Šค๋ฅผ ํ˜ธํ™˜์‹œํ‚ค๊ณ , ๋” ๋งŽ์€ ์ข…๋ฅ˜์˜ ์„œ๋น„์Šค๋ฅผ ์ œ๊ณตํ•œ๋‹ค. ํ† ํฐ์„ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด, ์–ด๋–ค ๋””๋ฐ”์ด์Šค์—์„œ๋„, ๋„๋ฉ”์ธ์—์„œ๋„ ํ† ํฐ๋งŒ ์œ ํšจํ•˜๋‹ค๋ฉด ์š”์ฒญ์ด ์ •์ƒ์ ์œผ๋กœ ์ฒ˜๋ฆฌ๋œ๋‹ค.

JSON Web Token

JWT๋Š” ์›นํ‘œ์ค€(RFC7519)์œผ๋กœ์„œ JSON ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•ด ๊ฐ€๋ณ๊ณ  ์ž๊ฐ€ ์ˆ˜์šฉ์ ์ธ ๋ฐฉ์‹์œผ๋กœ ์ •๋ณด๋ฅผ ์•ˆ์ „์„ฑ ์žˆ๊ฒŒ ์ „๋‹ฌํ•ด์ค€๋‹ค.

  • ๋งŽ์€ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด์—์„œ ์ง€์›(C, Java, Python, C++, R, JavaScript, Ruby...)

  • ์ž๊ฐ€ ์ˆ˜์šฉ์ (self-contained) : JWT๋Š” ํ•„์š”ํ•œ ๋ชจ๋“  ์ •๋ณด๋ฅผ ์ž์ฒด์ ์œผ๋กœ ์ง€๋‹ˆ๊ณ  ์žˆ๋‹ค.

  • ์‰ฝ๊ฒŒ ์ „๋‹ฌ ๊ฐ€๋Šฅ : ์›น ์„œ๋ฒ„์˜ ๊ฒฝ์šฐ HTTP ํ—ค๋”์— ๋„ฃ์–ด ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๊ณ , URL์˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋‹ค.

์ฃผ๋กœ ํšŒ์› ์ธ์ฆ์ด๋‚˜, ์•ˆ์ •์„ฑ์žˆ๊ฒŒ ์ •๋ณด ๊ต๋ฅ˜๋ฅผ ํ•  ๋•Œ ์‚ฌ์šฉ๋œ๋‹ค.

https://velopert.com/wp-content/uploads/2016/12/jwt.png
{
  "typ" : "JWT",
  "alg": "HS256"
}
  • typ : ํ† ํฐ์˜ ํƒ€์ž…์„ ์ง€์ •

  • alg : ํ•ด์‹ฑ ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์ง€์ •(HMAC SHA256 , RSA)

Payload

Payload ๋ถ€๋ถ„์—๋Š” ํ† ํฐ์— ๋‹ด์„ ์ •๋ณด๊ฐ€ ๋“ค์–ด์žˆ๋”ฐ. ์—ฌ๊ธฐ์— ๋‹ด๋Š” ์ •๋ณด์˜ ํ•œ ์กฐ๊ฐ์„ clame ์ด๋ผ ํ•˜๋ฉฐ, ์ด๋Š” name,value ์Œ์œผ๋กœ ์ด๋ฃจ์–ด์ ธ์žˆ๋‹ค.

{
  "sub": "1234567890",
  "name": "Dahye Jeong",
  "iat": 1516239022
}

ํด๋ ˆ์ž„์€ ํฌ๊ฒŒ ์„ธ๋ถ„๋ฅ˜๋กœ ๋‚˜๋‰œ๋‹ค.

Registered claim

์„œ๋น„์Šค์—์„œ ํ•„์š”ํ•œ ์ •๋ณด๋“ค์ด ์•„๋‹Œ, ํ† ํฐ์— ๋Œ€ํ•œ ์ •๋ณด๋“ค์„ ๋‹ด๊ธฐ ์œ„ํ•ด ์ด๋ฏธ ์ด๋ฆ„์ด ์ •ํ•ด์ง„ ํด๋ ˆ์ž„๋“ค์ด๋‹ค. ๋“ฑ๋ก๋œ ํด๋ ˆ์ž„์˜ ์‚ฌ์šฉ์€ ๋ชจ๋‘ ์„ ํƒ์ (optional)ํ•˜๋‹ค.

registered claim

์„ค๋ช…

iss

ํ† ํฐ ๋ฐœ๊ธ‰์ž(issuer)

sub

ํ† ํฐ ์ œ๋ชฉ(subject)

aud

ํ† ํฐ ๋Œ€์ƒ์ž(audience)

exp

ํ† ํฐ๋งŒ๋ฃŒ์‹œ๊ฐ„(expriation)

nbf

Not Before๋กœ ํ† ํฐ ํ™œ์„ฑ ๋‚ ์งœ์™€ ๋น„์Šทํ•œ ๊ฐœ๋…

iat

ํ† ํฐ์ด ๋ฐœ๊ธ‰๋œ ์‹œ๊ฐ„(issued at)

jti

JWT์˜ ๊ณ ์œ  ์‹๋ณ„์ž๋กœ, ์ค‘๋ณต์ฒ˜๋ฆฌ๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ

Public Claim

๊ณต๊ฐœ ํด๋ ˆ์ž„๋“ค์€ ์ถฉ๋Œ์ด ๋ฐฉ์ง€๋œ ์ด๋ฆ„์„ ๊ฐ–๊ณ  ์žˆ์–ด์•ผํ•œ๋‹ค. ์ถฉ๋Œ ๋ฐฉ์ง€๋ฅผ ์œ„ํ•ด์„œ๋Š” ํด๋ ˆ์ž„์ด๋ฆ„์„ URI ํ˜•์‹์œผ๋กœ ์ง“๋Š”๋‹ค.

{
    "https://velopert.com/jwt_claims/is_admin": true
}

Private Claim

ํด๋ผ์ด์–ธํŠธ <-> ์„œ๋ฒ„ ๊ฐ„์— ํ˜‘์˜ํ•˜์— ์‚ฌ์šฉ๋˜๋Š” ํด๋ ˆ์ž„๋“ค์ด๋‹ค. ๊ณต๊ฐœ ํด๋ ˆ์ž„๊ณผ ๋‹ฌ๋ฆฌ ์ด๋ฆ„์ด ์ค‘๋ณต๋˜์–ด ์ถฉ๋Œ ๋  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ์ฃผ์˜ํ•ด์•ผํ•œ๋‹ค.

"name": "Dahye Jeong",

Signature

์„œ๋ช…์€ Header์˜ ์ธ์ฝ”๋”ฉ๋œ ๊ฐ’๊ณผ Payload์˜ ์ธ์ฝ”๋”ฉ๋œ ๊ฐ’์„ ํ•ฉ์นœ ํ›„ ์ฃผ์–ด์ง„ ๋น„๋ฐ€ํ‚ค๋กœ Hash๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  your-256-bit-secret
);

์ด๋ ‡๊ฒŒ ๋งŒ๋“  ํ•ด์‰ฌ๋ฅผ base64ํ˜•ํƒœ๋กœ ๋‚˜ํƒ€๋‚ด๋ฉด๋œ๋‹ค.

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ƒ์„ฑ๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

base64

// payload or header ์ƒ์„ฑํ•˜๊ธฐ
const encodedPayload = new Buffer(JSON.stringify(payload))
                            .toString('base64')
                            .replace('=', '');
const crypto = require('crypto');
const signature = crypto.createHmac('sha256', 'secret')
             .update(encodedHeader + '.' + encodedPayload)
             .digest('base64')
             .replace('=', '');

๋‹ค์Œ๊ณผ ๊ฐ™์ด base64๋ฅผ ์ด์šฉํ•ด ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

์ฐธ๊ณ 

Last updated

Was this helpful?