๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐Ÿ“ฆ Spring/๐Ÿ—‚๏ธ ์ธ์ฆ&์ธ๊ฐ€

[Spring] ์„ธ์…˜ ๊ธฐ๋ฐ˜ ์ธ์ฆ๊ณผ ๋‹ค๋ฅธ JWT์˜ ๋™์ž‘ ์›๋ฆฌ

by ์‚๋šค์˜ค๋ฆฌ 2025. 9. 22.

๐Ÿ“Œ JWT(Json Web Token) ํŠน์ง•๊ณผ ์ฃผ์˜ ์‚ฌํ•ญ

JWT(Json Web Token)๋Š” JSON ํ˜•์‹์„ ๊ธฐ๋ฐ˜์œผ๋กœ ์•ˆ์ „ํ•˜๊ฒŒ ์ „์†กํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ๋˜๋Š” ํด๋ ˆ์ž„ ๊ธฐ๋ฐ˜ ํ† ํฐ์ด๋‹ค.
์ฃผ๋กœ ์ธ์ฆ(Authentication)๊ณผ ์ธ๊ฐ€(Authorization) ๊ณผ์ •์—์„œ ํ™œ์šฉ๋˜๋ฉฐ, ์„œ๋ฒ„๊ฐ€ ์ƒํƒœ๋ฅผ ์ €์žฅํ•˜์ง€ ์•Š๋Š” Stateless ์ธ์ฆ ๋ฐฉ์‹์„ ์ œ๊ณตํ•œ๋‹ค.
๊ตฌ์กฐ๋Š” Header, Payload, Signature ์„ธ ๋ถ€๋ถ„์œผ๋กœ ๋‚˜๋‰˜๋ฉฐ, ์„œ๋ช…(Signature)์„ ํ†ตํ•ด ๋ณ€์กฐ ์—ฌ๋ถ€๋ฅผ ๊ฒ€์ฆํ•  ์ˆ˜ ์žˆ๋‹ค.

 

โœ… ์ฃผ์š” ๊ฐœ๋… ์ •๋ฆฌ

๊ตฌ๋ถ„ ์„ค๋ช…
Header ํ† ํฐ์˜ ํƒ€์ž…(JWT)๊ณผ ํ•ด์‹ฑ ์•Œ๊ณ ๋ฆฌ์ฆ˜(HS256, RS256 ๋“ฑ)์„ ๋ช…์‹œ
Payload ์‚ฌ์šฉ์ž ์ •๋ณด, ๊ถŒํ•œ, ๋งŒ๋ฃŒ ์‹œ๊ฐ„(exp) ๋“ฑ ํด๋ ˆ์ž„(Claims) ํฌํ•จ
Signature Header + Payload๋ฅผ ๋น„๋ฐ€ํ‚ค๋กœ ์„œ๋ช…ํ•˜์—ฌ ๋ณ€์กฐ ์—ฌ๋ถ€ ๊ฒ€์ฆ
ํด๋ ˆ์ž„(Claims) ํ† ํฐ์— ๋‹ด๊ธด ์ •๋ณด ๋‹จ์œ„. ๋“ฑ๋ก๋œ ํด๋ ˆ์ž„(iss, sub, exp ๋“ฑ)๊ณผ ์ปค์Šคํ…€ ํด๋ ˆ์ž„์œผ๋กœ ๊ตฌ๋ถ„

 

๐Ÿ’ก JWT ํŠน์ง•

1. Stateless ์ธ์ฆ

  • ์„œ๋ฒ„๊ฐ€ ์„ธ์…˜์„ ์ €์žฅํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ ํ™•์žฅ์„ฑ๊ณผ ์„œ๋ฒ„ ์ด์ค‘ํ™” ํ™˜๊ฒฝ์— ์ ํ•ฉ
  • ์„ธ์…˜ ๋ถˆ์ผ์น˜ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š”๋‹ค.

2. ์ž์ฒด ํฌํ•จ (Self-contained)

  • ํ† ํฐ์— ํ•„์š”ํ•œ ์ •๋ณด(์‚ฌ์šฉ์ž, ๊ถŒํ•œ ๋“ฑ)๋ฅผ ํฌํ•จํ•˜๊ณ  ์žˆ์–ด DB ์กฐํšŒ ์—†์ด ์ธ๊ฐ€ ๊ฐ€๋Šฅ

3. ๊ตฌ์กฐ์  ๋‹จ์ˆœ์„ฑ

  • Base64URL ์ธ์ฝ”๋”ฉ ๋œ ๋ฌธ์ž์—ด๋กœ, HTTP ํ—ค๋”๋‚˜ URL ์ฟผ๋ฆฌ์ŠคํŠธ๋ง์— ์‰ฝ๊ฒŒ ๋‹ด์•„ ์ „์†ก ๊ฐ€๋Šฅ

4. ๋ฒ”์šฉ์„ฑ

  • OAuth2, OpenID Connect ๋“ฑ ๋‹ค์–‘ํ•œ ์ธ์ฆ ํ‘œ์ค€์—์„œ ํ™œ์šฉ

 

โš ๏ธ JWT ์‚ฌ์šฉ ์‹œ ์ฃผ์˜ ์‚ฌํ•ญ

ํ•ญ๋ชฉ ์„ค๋ช…
๋ฏผ๊ฐ ์ •๋ณด ์ €์žฅ ๊ธˆ์ง€ Payload๋Š” Base64 ๋””์ฝ”๋”ฉ์œผ๋กœ ๋ˆ„๊ตฌ๋‚˜ ๋ณผ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ๋น„๋ฐ€๋ฒˆํ˜ธ, ์ฃผ๋ฏผ๋ฒˆํ˜ธ ๋“ฑ ๋ฏผ๊ฐํ•œ ์ •๋ณด๋Š” ๋‹ด์ง€ ์•Š์•„์•ผ ํ•œ๋‹ค.
์‹œํฌ๋ฆฟ ํ‚ค ๊ด€๋ฆฌ ์ถฉ๋ถ„ํžˆ ๋ณต์žกํ•œ ํ‚ค๋ฅผ ์‚ฌ์šฉํ•˜๊ณ , ์•ˆ์ „ํ•œ ํ‚ค ๊ด€๋ฆฌ ์ „๋žต(KMS, Vault ๋“ฑ)์„ ์ ์šฉํ•ด์•ผ ํ•œ๋‹ค.
ํ† ํฐ ํƒˆ์ทจ ์œ„ํ—˜ XSS, CSRF, ๋กœ์ปฌ ์Šคํ† ๋ฆฌ์ง€ ์ €์žฅ ์‹œ ํƒˆ์ทจ ์œ„ํ—˜ → HttpOnly Cookie, Refresh Token Rotation ๊ณ ๋ ค
๋งŒ๋ฃŒ ์‹œ๊ฐ„ ์„ค์ • ํ† ํฐ์ด ๋„ˆ๋ฌด ๋นจ๋ฆฌ ๋งŒ๋ฃŒ๋˜๋ฉด UX ์ €ํ•˜, ๋„ˆ๋ฌด ๊ธธ๋ฉด ๋ณด์•ˆ ์ทจ์•ฝ → Sliding Session ์ „๋žต ํ™œ์šฉ ๊ฐ€๋Šฅ
None ์•Œ๊ณ ๋ฆฌ์ฆ˜ ๊ณต๊ฒฉ alg:none ์ทจ์•ฝ์  ์•…์šฉ ๋ฐฉ์ง€ → ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ ํƒ ์‹œ ๊ฒ€์ฆ ํ•„์š”
ํ† ํฐ ํฌ๊ธฐ ๋ฌธ์ œ JWT๋Š” ๊ธธ์ด๊ฐ€ ๊ธธ์–ด์งˆ ์ˆ˜ ์žˆ์–ด ๋„คํŠธ์›Œํฌ ๋น„์šฉ ์ฆ๊ฐ€ → ๋ถˆํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋Š” ์ตœ์†Œํ™”
Revocation ๋ถˆ๊ฐ€ ํ•œ ๋ฒˆ ๋ฐœ๊ธ‰๋œ ํ† ํฐ์€ ๋งŒ๋ฃŒ ์ „๊นŒ์ง€ ๋ฌดํšจํ™” ์–ด๋ ต๊ธฐ ๋•Œ๋ฌธ์—, ๋ธ”๋ž™๋ฆฌ์ŠคํŠธ/ํ™”์ดํŠธ๋ฆฌ์ŠคํŠธ๋‚˜ Redis ์บ์‹œ ํ™œ์šฉ ํ•„์š”

 

 

๐Ÿงช ์˜ˆ์‹œ (JWT ๊ตฌ์กฐ)

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9   // Header
.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkhvbmcgR2lsZG9uZyIsImlhdCI6MTUxNjIzOTAyMn0 // Payload
.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c   // Signature

 

๐Ÿง  ์š”์•ฝ

  • JWT๋Š” ํด๋ ˆ์ž„ ๊ธฐ๋ฐ˜ ํ† ํฐ์œผ๋กœ ์ธ์ฆ/์ธ๊ฐ€์— ๋„๋ฆฌ ์‚ฌ์šฉ
  • ์žฅ์ : Stateless, ์ž์ฒด ํฌํ•จ, ํ™•์žฅ์„ฑ
  • ๋‹จ์ : ํƒˆ์ทจ ์œ„ํ—˜, ๋ฏผ๊ฐ ์ •๋ณด ์ €์žฅ ๋ถˆ๊ฐ€, ๋งŒ๋ฃŒ ์ „ ๊ฐ•์ œ ํ๊ธฐ ์–ด๋ ค์›€
  • ๋ณด์•ˆ ๊ฐ•ํ™”๋ฅผ ์œ„ํ•ด ์งง์€ Access Token + Refresh Token + ์•ˆ์ „ํ•œ ํ‚ค ๊ด€๋ฆฌ ์ „๋žต์ด ํ•„์š”