[Spring] JWT ๊ตฌ์กฐ (Header, Payload, Signature)

์กฐ๋ฏผ๊ฒฝยท2025๋…„ 3์›” 12์ผ
0

Spring

๋ชฉ๋ก ๋ณด๊ธฐ
6/13

Chapter 1 : ๐Ÿ”‘ JWT ๊ตฌ์กฐ (Header, Payload, Signature)


๐Ÿ“Œ JWT(JSON Web Token)์€ ์„ธ ๋ถ€๋ถ„์œผ๋กœ ๊ตฌ์„ฑ๋œ๋‹ค.

HEADER.PAYLOAD.SIGNATURE


๐Ÿ‘€ JWT๋Š” .(์ )์œผ๋กœ ๊ตฌ๋ถ„๋œ 3๊ฐœ์˜ ํŒŒํŠธ๋กœ ์ด๋ฃจ์–ด์ ธ ์žˆ๋‹ค.
๊ฐ ๋ถ€๋ถ„์€ Base64Url๋กœ ์ธ์ฝ”๋”ฉ๋˜์–ด ์ „๋‹ฌ๋œ๋‹ค.



๐Ÿ“‘ Header (ํ—ค๋”)

๐Ÿ“Œ JWT์˜ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ํฌํ•จํ•˜๋Š” ๋ถ€๋ถ„์ด๋‹ค.
๋ณดํ†ต ์‚ฌ์šฉํ•  ์•Œ๊ณ ๋ฆฌ์ฆ˜(HS256, RS256 ๋“ฑ)๊ณผ ํ† ํฐ ํƒ€์ž…(JWT)์„ ๋‹ด๋Š”๋‹ค.

{
  "alg": "HS256",  // ์‚ฌ์šฉํ•  ํ•ด์‹ฑ ์•Œ๊ณ ๋ฆฌ์ฆ˜ (HMAC SHA256)
  "typ": "JWT"     // ํ† ํฐ ํƒ€์ž…
}

โœ… Base64Url๋กœ ์ธ์ฝ”๋”ฉ๋œ ์˜ˆ์‹œ

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9




๐Ÿ“œ Payload (ํŽ˜์ด๋กœ๋“œ)

๐Ÿ“Œ ์‚ฌ์šฉ์ž์˜ ์ฃผ์š” ์ •๋ณด(Claim)๊ฐ€ ํฌํ•จ๋˜๋Š” ๋ถ€๋ถ„์ด๋‹ค.
์ฆ‰, JWT์˜ ๋ณธ๋ฌธ ์—ญํ• ์„ ํ•œ๋‹ค.


โœ… Claim์ด๋ž€?

๐Ÿ“Œ Claim(ํด๋ ˆ์ž„)์€ JWT (JSON Web Token) ๋‚ด๋ถ€์— ํฌํ•จ๋œ ์‚ฌ์šฉ์ž ์ •๋ณด(๋ฐ์ดํ„ฐ) ํŽ˜์ด๋กœ๋“œ๋ฅผ ์˜๋ฏธํ•œ๋‹ค.
์ฆ‰, JWT ์•ˆ์— ๋“ค์–ด์žˆ๋Š” Key-Value ํ˜•ํƒœ์˜ ๋ฐ์ดํ„ฐ๋ผ๊ณ  ๋ณด๋ฉด ๋œ๋‹ค.


โœ… Claim(ํด๋ ˆ์ž„) ์ข…๋ฅ˜

๐Ÿ”น Registered Claims (๋“ฑ๋ก๋œ ํด๋ ˆ์ž„) โ†’ JWT ํ‘œ์ค€์—์„œ ์ •์˜ํ•œ ํด๋ ˆ์ž„

  • sub (์ฃผ์ œ), iss (๋ฐœ๊ธ‰์ž), exp (๋งŒ๋ฃŒ ์‹œ๊ฐ„)

๐ŸŒ Public Claims (๊ณต๊ฐœ ํด๋ ˆ์ž„) โ†’ ์ผ๋ฐ˜์ ์œผ๋กœ ์‚ฌ์šฉ๋˜๋Š” ํด๋ ˆ์ž„

  • name, email, role

๐Ÿ”’ Private Claims (๋น„๊ณต๊ฐœ ํด๋ ˆ์ž„) โ†’ ์„œ๋น„์Šค์—์„œ ์ž์ฒด์ ์œผ๋กœ ์ •์˜ํ•œ ํด๋ ˆ์ž„

  • userId, membershipType

{
  "sub": "1234567890",  // ์‚ฌ์šฉ์ž ID (๋“ฑ๋ก๋œ ํด๋ ˆ์ž„)
  "name": "John Doe",   // ์‚ฌ์šฉ์ž ์ด๋ฆ„ (๊ณต๊ฐœ ํด๋ ˆ์ž„)
  "admin": true,        // ๊ด€๋ฆฌ์ž ์—ฌ๋ถ€ (๋น„๊ณต๊ฐœ ํด๋ ˆ์ž„)
  "exp": 1712345678     // ๋งŒ๋ฃŒ ์‹œ๊ฐ„ (UNIX timestamp, ๋“ฑ๋ก๋œ ํด๋ ˆ์ž„)
}

โœ… Base64Url๋กœ ์ธ์ฝ”๋”ฉ๋œ ์˜ˆ์‹œ

eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9

โœ… JWT์—์„œ Claim ์ถ”์ถœํ•˜๊ธฐ

๐Ÿ’ป JwtFilter.class

Claims claims = jwtUtil.extractClaims(jwt);

๐Ÿ’ป JwtUtill.class

@PostConstruct
public void init() {
	byte[] bytes = Base64.getDecoder().decode(secretKey);
	key = Keys.hmacShaKeyFor(bytes);
}
    
...

public Claims extractClaims(String token) { 
	return Jwts.parserBuilder()
		.setSigningKey(key)
		.build()
		.parseClaimsJws(token)
		.getBody();
}




๐Ÿ” Signature (์„œ๋ช…)

๐Ÿ“Œ ํ† ํฐ์˜ ๋ฌด๊ฒฐ์„ฑ์„ ๋ณด์žฅํ•˜๊ธฐ ์œ„ํ•œ ๋””์ง€ํ„ธ ์„œ๋ช…์ด๋‹ค.
์ฆ‰, ํ† ํฐ์ด ์œ„๋ณ€์กฐ๋˜์ง€ ์•Š์•˜์Œ์„ ๊ฒ€์ฆํ•˜๋Š” ์—ญํ• ์„ ํ•œ๋‹ค.

์„œ๋ช…์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ƒ์„ฑ๋œ๋‹ค.

HMACSHA256(
  base64UrlEncode(header) + "." + base64UrlEncode(payload),
  secretKey
)

โœ… ์„œ๋ช…์ด ํฌํ•จ๋œ JWT ์˜ˆ์‹œ

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9
.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c




๐ŸŽฏ ์ •๋ฆฌ

๐Ÿ›ก๏ธ JSON Web Token (JWT)

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0. KMUFsIDTnFmyG3nMiGM6H9FNFUROf3wh7SmqJp-QV30


๐Ÿ“‘ Header โ†’ Algorithm & Token Type

{
ย ย ย ย "alg": "HS256",
ย ย ย ย "typ": "JWT"
}


๐Ÿ“œ Payload โ†’ Data(Claim)

{
ย ย ย ย "sub": "1234567890",
ย ย ย ย "name": "John Doe",
ย ย ย ย "admin": true,
ย ย ย ย "iat": 1516239022
}


๐Ÿ” Signature โ†’ ํ† ํฐ์ด ์œ„๋ณ€์กฐ๋˜์ง€ ์•Š์•˜์Œ์„ ๊ฒ€์ฆ

a-string-secret-at-least-256-bits-long



๐Ÿš€ ๊ฒฐ๋ก 

JWT๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ธ์ฆ๋œ ์ •๋ณด๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ํด๋ผ์ด์–ธํŠธ์— ์ €์žฅํ•˜๊ณ  ์ฃผ๊ณ ๋ฐ›์„ ์ˆ˜ ์žˆ์Œ! ๐Ÿ”ฅ

0๊ฐœ์˜ ๋Œ“๊ธ€