JSON Web Token - MoonGyeongHyeon/Keyword_Study GitHub Wiki
JSON Web Token
JWT?
JSON Web Token(JWT)μ μΉνμ€μΌλ‘μ¨ λ κ°μ²΄μμ JSON κ°μ²΄λ₯Ό μ¬μ©νμ¬ κ°λ³κ³ μκ°μμ©μ μΈ(self-contained) λ°©μμΌλ‘ μ 보λ₯Ό μμ μ± μκ² μ λ¬ν΄μ€λ€.
μκ°μμ©μ
JWTλ νμν λͺ¨λ μ 보λ₯Ό μ체μ μΌλ‘ μ§λκ³ μλ€. JWT μμ€ν
μμ λ°κΈλ ν ν°μ ν ν°μ λν κΈ°λ³Έ μ 보, μ λ¬ν μ 보(μ μ μ 보 λ±), ν ν°μ΄ κ²μ¦λλ€λ κ²μ μ¦λͺ
ν΄μ£Όλ signature
λ₯Ό ν¬ν¨νκ³ μλ€.
μλ§μ νλ‘κ·Έλλ° μΈμ΄ μ§μ
C, Java, Python, C++, C#, PHP, Ruby, Javascript λ± λλΆλΆμ μ£Όλ₯ νλ‘κ·Έλλ° μΈμ΄μμ μ§μλλ€.
μμ¬μ΄ μ λ¬
JWTλ λ κ°μ²΄ μ¬μ΄μμ μμ½κ² μ λ¬λ μ μλ€. μΉ μλ²μ κ²½μ°, HTTPμ ν€λμ λ£μ΄μ μ λ¬ν μλ μκ³ URLμ νλΌλ―Έν°λ‘ μ λ¬ν μλ μλ€.
μ¬μ©νκΈ° μ’μ κ²½μ°
λ€μκ³Ό κ°μ μν©μμ JWTλ μ μ©νκ² μ¬μ©λ μ μλ€.
- νμ μΈμ¦: JWTλ₯Ό μ¬μ©νλ κ°μ₯ νν μλ리μ€λ€. μ μ κ° λ‘κ·ΈμΈμ νλ©΄, μλ²λ μ μ μ μ 보μ κΈ°λ°ν ν ν°μ λ°κΈνμ¬ μ μ μκ² μ λ¬νλ€. κ·Έ ν, μ μ κ° μλ²μ μμ²ν λλ§λ€ JWTλ₯Ό ν¬ν¨νμ¬ μ λ¬νλ€. μλ²κ° ν΄λΌμ΄μΈνΈμκ²μ μμ²μ λ°μ λλ§λ€ ν΄λΉ ν ν°μ΄ μ ν¨νκ³ μΈμ¦λλμ§λ₯Ό κ²μ¦νκ³ , μ μ κ° μμ²ν μμ μ κΆνμ΄ μλμ§ νμΈνμ¬ μμ μ μ²λ¦¬νλ€. μλ² μΈ‘μμλ μ μ μ μΈμ μ μ μ§ν νμκ° μλ€. μ¦, μ μ κ° λ‘κ·ΈμΈλμ΄μλμ§ μλμ΄μλμ§ μ κ²½μΈ νμκ° μκ³ , μ μ κ° μμ²νμ λ ν ν°λ§ νμΈνλ©΄ λλ, μΈμ μ κ΄λ¦¬ν νμκ° μμ΄ μλ² μμμ μλ μ μλ€.
- μ 보 κ΅λ₯: JWTλ λ κ°μ²΄ μ¬μ΄μμ μμ μ±μκ² μ 보λ₯Ό κ΅ννκΈ°μ μ’μ λ°©λ²μ΄λ€. κ·Έ μ΄μ λ, μ λ³΄κ° signμ΄ λΌμκΈ° λλ¬Έμ μ 보λ₯Ό λ³΄λΈ μ΄κ° λ°λμ§ μμλμ§, λ μ λ³΄κ° λμ€μ μ‘°μλμ§λ μμλμ§ κ²μ¦ν μ μλ€.
JWT νμ
JWTλ .
μ ꡬλΆμλ‘ 3κ°μ§ λ¬Έμμ΄λ‘ ꡬμ±λμ΄ μλ€. ꡬ쑰λ λ€μκ³Ό κ°λ€.
ν€λ(header)
'ν€λ'λ λ κ°μ§μ μ 보λ₯Ό μ§λκ³ μλ€.
- typ: ν ν°μ νμ μ μ§μ νλ€. "JWT" μ΄λ€.
- alg: ν΄μ± μκ³ λ¦¬μ¦μ μ§μ νλ€. 보ν΅
HMAC SHA256
λλRSA
κ° μ¬μ©λλ©°, μ΄ μκ³ λ¦¬μ¦μ ν ν°μ κ²μ¦ν λ μ¬μ©λλ signature λΆλΆμμ μ¬μ©λλ€.
{
"typ": "JWT",
"alg": "HS256" // HMAC SHA256 λ°©μ.
}
μ μ 보λ₯Ό base64
λ‘ μΈμ½λ©νλ©΄ λ€μκ³Ό κ°μ κ²°κ³Όκ° λμ¨λ€.
// node.js νκ²½
var Buffer = require('Buffer');
const header = {
"typ": "JWT",
"alg": "HS256"
};
// encode to base64
const encodedHeader = new Buffer(JSON.stringify(header))
.toString('base64')
.replace('=', '');
console.log('header: ',encodedHeader);
// header: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
- κ°νΉ, base64λ‘ μΈμ½λ©ν λ 'dA=='μ²λΌ λ€μ '=' λ¬Έμκ° νλ μ΄μ λΆμ λκ° μλ€. μ΄ λ¬Έμλ base64 μΈμ½λ©μ 'padding λ¬Έμ'λΌκ³ λΆλ₯Έλ€. JWT ν ν°μ κ°λ URL νλΌλ―Έν°λ‘ μ λ¬λ λλ μλλ°, μ΄λ¬ν padding λ¬Έμλ url-safeνμ§ λͺ»νκΈ° λλ¬Έμ μ κ±°ν΄μ£Όμ΄μΌ νλ€. padding λ¬Έμκ° νλ μκΈΈλλ μκ³ , λ κ°κ° μκΈΈ λλ μλλ° μ λΆ μ κ±°ν΄μ€λ€. (μ κ±°ν΄λ λμ½λ©ν λ μ ν λ¬Έμ κ° λμ§ μλλ€.)
μ 보(payload)
'μ 보'λ ν ν°μ λ΄μ μ λ³΄κ° λ€μ΄μλ€. μ¬κΈ°μ λ΄λ μ 보μ ν μ‘°κ°μ ν΄λ μ(claim)
μ΄λΌ λΆλ₯΄κ³ , μ΄λ 'name / value'μ ν μμΌλ‘ μ΄λ£¨μ΄μ Έ μλ€. ν ν°μλ μ¬λ¬ κ°μ ν΄λ μμ λ£μ μ μλ€.
ν΄λ μμ μ’ λ₯λ λ€μκ³Ό κ°μ΄ ν¬κ² μΈ λΆλ₯λ‘ λλμ΄μ Έ μλ€.
-
λ±λ‘λ(registered) ν΄λ μ: λ±λ‘λ ν΄λ μλ€μ μλΉμ€μ νμν μ 보λ€μ΄ μλ, ν ν°μ λν μ 보λ€μ λ΄κΈ° μν΄ μ΄λ¦μ΄ μ΄λ―Έ μ ν΄μ§ ν΄λ μλ€μ΄λ€. λ±λ‘λ ν΄λ μμ μ¬μ©μ λͺ¨λ μ νμ (Optional)μ΄λ©°, μ΄μ ν¬ν¨λ ν΄λ μλ€μ λ€μκ³Ό κ°λ€.
iss
: ν ν° λ°κΈμ (issuer)sub
: ν ν° μ λͺ© (subject)aud
: ν ν° λμμ (audience)exp
: ν ν°μ λ§λ£μκ° (expiration), μκ°μ NumericDate νμ(μ: 1480849147370)μΌλ‘ λμ΄μμ΄μΌ νλ©°, μΈμ λ νμ¬ μκ°λ³΄λ€ μ΄νλ‘ μ€μ λμ΄ μμ΄μΌ νλ€.nbf
: 'Not Before'λ₯Ό μλ―Ένλ©°, ν ν°μ νμ± λ μ§μ λΉμ·ν κ°λ μ΄λ€. μ¬κΈ°μλ NumericDate νμμΌλ‘ λ μ§λ₯Ό μ§μ νλ©°, μ΄ λ μ§κ° μ§λκΈ° μ κΉμ§λ ν ν°μ΄ μ²λ¦¬λμ§ μμ΅λλ€.iat
: ν ν°μ΄ λ°κΈλ μκ°(issued at), μ΄ κ°μ μ¬μ©νμ¬ ν ν°μage
κ° μΌλ§λ λλμ§ νλ¨ν μ μλ€.jti
: JWTμ κ³ μ μλ³μλ‘, μ£Όλ‘ μ€λ³΅μ μΈ μ²λ¦¬λ₯Ό λ°©μ§νκΈ° μν΄ μ¬μ©λλ€. μΌνμ© ν ν°μ μ¬μ©νλ©΄ μ μ©νλ€.
-
곡κ°(public) ν΄λ μ: κ³΅κ° ν΄λ μμ JWT μ¬μ©μ κ° μΆ©λμ λ°©μ§νκΈ° μν΄ μ¬μ©νλ€. κ·Έλ¬λ―λ‘ μΆ©λμ΄ λ°©μ§λ(collision-resistant) μ΄λ¦μ κ°μ§κ³ μμ΄μΌ νλ€. μΆ©λμ λ°©μ§νκΈ° μν΄μ URI νμμ΄λ λ€μ μ€νμ΄μ€λ₯Ό νμ©νμ¬ ν΄λ μ μ΄λ¦μ μ§λλ€.
{ "https://velopert.com/jwt_claims/is_admin": true }
-
λΉκ³΅κ°(private) ν΄λ μ: μμΈ‘ κ°(ν΄λΌμ΄μΈνΈ <-> μλ²) νμ νμ μ¬μ©λλ ν΄λ μ μ΄λ¦λ€μ΄λ€. κ³΅κ° ν΄λ μκ³Όλ λ¬λ¦¬ μ΄λ¦μ΄ μ€λ³΅λμ΄ μΆ©λλ μ μμΌλ μ¬μ©ν λ μ μν΄μΌ νλ€. μ λ¬νκ³ μ νλ μ 보λ₯Ό λ΄λλ€.
{ "username": "velopert" }
μμ±λ μ 보λ μλμ κ°λ€.
{
"iss": "velopert.com", // λ±λ‘λ ν΄λ μ.
"exp": "1485270000000", // λ±λ‘λ ν΄λ μ.
"https://velopert.com/jwt_claims/is_admin": true, // κ³΅κ° ν΄λ μ.
"userId": "11028373727102", // λΉκ³΅κ° ν΄λ μ.
"username": "velopert" // λΉκ³΅κ° ν΄λ μ.
}
base64
μΈμ½λ© κ²°κ³Όλ μλμ κ°λ€.
var Buffer = require('Buffer');
const payload = {
"iss": "velopert.com",
"exp": "1485270000000",
"https://velopert.com/jwt_claims/is_admin": true,
"userId": "11028373727102",
"username": "velopert"
};
// encode to base64
const encodedPayload = new Buffer(JSON.stringify(payload))
.toString('base64')
.replace('=', '');
console.log('payload: ',encodedPayload);
// payload: eyJpc3MiOiJ2ZWxvcGVydC5jb20iLCJleHAiOiIxNDg1MjcwMDAwMDAwIiwiaHR0cHM6Ly92ZWxvcGVydC5jb20vand0X2NsYWltcy9pc19hZG1pbiI6dHJ1ZSwidXNlcklkIjoiMTEwMjgzNzM3MjcxMDIiLCJ1c2VybmFtZSI6InZlbG9wZXJ0In0
μλͺ (signature)
JWTμ λ§μ§λ§ λΆλΆμ λ°λ‘ 'μλͺ 'μ΄λ€. μλͺ μ ν€λμ μΈμ½λ© κ°κ³Ό μ 보μ μΈμ½λ© κ°μ ν©μΉ ν μ£Όμ΄μ§ λΉλ° ν€λ‘ ν΄μ±νμ¬ μμ±νλ€. μλͺ λΆλΆμ λ§λλ νλ¦μ λ€μκ³Ό κ°λ€.
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
ν€λμ μ 보μ μΈμ½λ© κ° μ¬μ΄μ "."μ λ£μ΄μ£Όκ³ ν©μΉλ€.
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ2ZWxvcGVydC5jb20iLCJleHAiOiIxNDg1MjcwMDAwMDAwIiwiaHR0cHM6Ly92ZWxvcGVydC5jb20vand0X2NsYWltcy9pc19hZG1pbiI6dHJ1ZSwidXNlcklkIjoiMTEwMjgzNzM3MjcxMDIiLCJ1c2VybmFtZSI6InZlbG9wZXJ0In0
μ€μ λ‘ μ€νμμΌ λ³΄λλ‘ νμ.
var Buffer = require('Buffer');
const crypto = require('crypto');
const header = {
"typ": "JWT",
"alg": "HS256"
};
// encode to base64
const encodedHeader = new Buffer(JSON.stringify(header))
.toString('base64')
.replace('=', '');
const payload = {
"iss": "velopert.com",
"exp": "1485270000000",
"https://velopert.com/jwt_claims/is_admin": true,
"userId": "11028373727102",
"username": "velopert"
};
// encode to base64
const encodedPayload = new Buffer(JSON.stringify(payload))
.toString('base64')
.replace('=', '');
const signature = crypto.createHmac('sha256', 'secret')
.update(encodedHeader + '.' + encodedPayload)
.digest('base64')
.replace('=', '');
console.log('signature: ',signature);
// signature: WE5fMufM0NDSVGJ8cAolXGkyB5RmYwCto1pQwDIqo2w
μ΅μ’ μ μΌλ‘, μΈ κ°μ "." ꡬλΆμλ‘ ν©μΉκ² λλ©΄ JWT μμ±μ΄ λλκ² λλ€.
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ2ZWxvcGVydC5jb20iLCJleHAiOiIxNDg1MjcwMDAwMDAwIiwiaHR0cHM6Ly92ZWxvcGVydC5jb20vand0X2NsYWltcy9pc19hZG1pbiI6dHJ1ZSwidXNlcklkIjoiMTEwMjgzNzM3MjcxMDIiLCJ1c2VybmFtZSI6InZlbG9wZXJ0In0.WE5fMufM0NDSVGJ8cAolXGkyB5RmYwCto1pQwDIqo2w
μμ κ°μ https://jwt.io/ λλ²κ±°μ λΆμ¬ λ£μΌλ©΄ λ€μκ³Ό κ°μ κ²°κ³Όλ₯Ό μ»μ μ μλ€. (JWT.IOλ λΈλΌμ°μ μμμ JWTμ κ²μ¦νκ³ μμ±ν μ μλλ‘ ν΄μ£Όλ λλ²κ±° μλΉμ€μ΄λ€.)
νλ¦
JWTμ μ΄μ©ν μμ²/μλ΅ νλ¦μ λ€μκ³Ό κ°λ€.
- ν΄λΌμ΄μΈνΈλ μλ²μ μΈμ¦(λ‘κ·ΈμΈ)μ μμ²νλ€.
- μΈμ¦μ΄ μ±κ³΅νλ€λ©΄, μλ²λ λΉλ° ν€λ₯Ό μ΄μ©νμ¬ JWTλ₯Ό μμ±νλ€.
- μλ²λ μμ±ν JWTλ₯Ό ν΄λΌμ΄μΈνΈ μΈ‘μΌλ‘ μ λ¬νλ€.
- ν΄λΌμ΄μΈνΈλ μΈμ¦ ν€λμ JWTλ₯Ό μΆκ°νμ¬ μμ²νλ€.
- μλ²λ JWTκ° μ ν¨(validation)νμ§ νμΈνκ³ , μ ν¨νλ€λ©΄ JWTλ₯Ό ν΅ν΄ ν΄λΌμ΄μΈνΈ μΈ‘ μ 보λ₯Ό μ»λλ€.
- μλ²λ μμ²ν μμμ ν΄λΌμ΄μΈνΈμ μ μ‘νλ€.
JWTλ₯Ό μ¬μ©νλ©΄?
μλ²μμ ν ν°μ΄ μ ν¨νλ€κ³ νλ¨νλ©΄ ν΄λ μλ€μ λμ½λ©ν΄ μ 보λ₯Ό κ°μ Έμ¬ μ μλ€. μ¦, μλ²μ λ©λͺ¨λ¦¬μ κ°μ λ³λμ μμΉμ λ°λ‘ κ΄λ¦¬λ₯Ό νμ§ μμλ λλ€λ κ²μ΄λ€. λ°λΌμ Stateless
ν μλ²λ₯Ό λ§λ€ μ μκ² λλ€. κ·Έλ¬λ―λ‘ μλ²μ νμ₯μ΄ μ©μ΄νκ³ μμ² λλ©μΈμ κ΄ν΄ λ¬Έμ κ° λμ§ μλλ€.
λ¨, ν΄λ μ μ μ΄ μ¦κ°νλ©΄ μμ°μ€λ½κ² ν ν°μ κΈΈμ΄κ° μ¦κ°νκ² λλλ°, μμ² ν€λμ ν ν°μ μ½μ νλ λ°©μμ΄λ―λ‘ κ³Όν μ€λ²ν€λκ° λ°μν μ μλ€.
Stateless vs Stateful
Stateful μλ²λ ν΄λΌμ΄μΈνΈμκ² μμ²μ λ°μ λλ§λ€ ν΄λΌμ΄μΈνΈμ μνλ₯Ό κ³μ μ μ§νκ³ , μ΄ μ 보λ₯Ό μλΉμ€ μ 곡μ μ΄μ©νλ€. μΈμ μ μ μ§νλ μΉ μλ²κ° μ΄μ ν΄λΉνλ€. μλ₯Ό λ€μ΄, μ μ κ° λ‘κ·ΈμΈμ νλ©΄ μΈμ μ λ‘κ·ΈμΈμ΄ λλ€κ³ μ μ₯μ ν΄λκ³ μλΉμ€λ₯Ό μ 곡ν λλ§λ€ κ·Έ λ°μ΄ν°λ₯Ό μ¬μ©νλ€. μ¬κΈ°μ μ΄ μΈμ μ μλ²μ λ©λͺ¨λ¦¬μ λ΄μ λλ μκ³ , λ°μ΄ν°λ² μ΄μ€μ λ΄μ λλ μλ€.
Stateless μλ²λ λ°λλ‘, μνλ₯Ό μ μ§νμ§ μλλ€. λ¨μ§ μλ²λ ν΄λΌμ΄μΈνΈ μΈ‘μμ λ€μ΄μ€λ μμ²μΌλ‘λ§ μμ μ μ²λ¦¬νλ€. μ΄λ κ² μνκ° μμ κ²½μ°, ν΄λΌμ΄μΈνΈμ μλ²μ μ°κ²°κ³ λ¦¬κ° μκΈ° λλ¬Έμ μλ²μ νμ₯μ±μ΄ λμμ§λ€.