๐ŸŽˆ๋…ผ๋ฌธ ์š”์•ฝ ํ”Œ๋žซํผ(์บก์Šคํ†ค) - 2(OAuth2 + JWT ์ธ์ฆ ์‹œ์Šคํ…œ)


๊ฐœ์š”

์šฐ์„  ์šฐ๋ฆฌ ํ”„๋กœ์ ํŠธ๋Š” ๋ณ„๋„์˜ ๋กœ๊ทธ์ธ/ํšŒ์›๊ฐ€์ž… ์ ˆ์ฐจ ์—†์ด ์š”์ฆ˜ ๋งŽ์ด ์‚ฌ์šฉํ•˜๋Š” Oauth2๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ๋‹ค. ์ด์œ ๋Š” ํ”„๋ก ํŠธ ์ชฝ ๊ตฌํ˜„ํ•  ๋•Œ๋„ ์ž‘์—…ํ•  ์–‘ ์ž์ฒด๊ฐ€ ์ค„์–ด๋“ค๊ธฐ๋„ ํ•˜๊ณ  ๋‚˜๋„ ๊ณต๋ถ€ํ•˜๋ฉด์„œ ํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™์•˜๋‹ค.

๊ธฐ๋ณธ ๋ชฉํ‘œ

Github OAuth๋กœ ๋กœ๊ทธ์ธํ•œ ์‚ฌ์šฉ์ž๋ฅผ ๋ฐ›์•„, ์„œ๋น„์Šค์— ๋“ฑ๋กํ•˜๊ณ , JWT ํ† ํฐ์„ ๋ฐœ๊ธ‰ํ•ด์„œ API ํ˜ธ์ถœํ•  ๋•Œ๋งˆ๋‹ค ์ธ์ฆํ•˜๋Š” ์‹œ์Šคํ…œ์„ ๊ตฌ์„ฑํ•  ๊ณ„ํš์ด๋‹ค.

ํฐ ํ๋ฆ„ ๋ณด๊ธฐ

  1. Client ์‚ฌ์šฉ์ž๊ฐ€ โ€œGitHub ๋กœ๊ทธ์ธ ๋ฒ„ํŠผ ํด๋ฆญโ€ -> /api/auth/github ์š”์ฒญ
  2. GitHub OAuth ํ™”๋ฉด์œผ๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ
  3. ์‚ฌ์šฉ์ž GitHub ๋กœ๊ทธ์ธ ํ›„ ์ธ์ฆ ์ฝ”๋“œ ๋ฐœ๊ธ‰
  4. /api/auth/github/callback?code=์ธ์ฆ์ฝ”๋“œ ํ˜ธ์ถœ
  5. ์ธ์ฆ์ฝ”๋“œ๋กœ GitHub AccessToken ์š”์ฒญ
  6. GitHub ์‚ฌ์šฉ์ž ์ •๋ณด ์กฐํšŒ
  7. DB์— ์‚ฌ์šฉ์ž ์ €์žฅ ๋˜๋Š” ์—…๋ฐ์ดํŠธ
  8. JWT AccessToken, RefreshToken ๋ฐœ๊ธ‰
  9. ํด๋ผ์ด์–ธํŠธ ์ €์žฅ ํ›„ ์ดํ›„ ์š”์ฒญ ์‹œ Authorization Bearer ํ† ํฐ์œผ๋กœ ์ธ์ฆ

์ฃผ์š” ๊ฐœ๋…

OAuth๋ž€?

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

JWT(Json Web Token)๋ž€?

์‚ฌ์šฉ์ž๊ฐ€ ์ธ์ฆ๋˜์—ˆ์Œ์„ ์ฆ๋ช…ํ•˜๋Š” โ€œ๋””์ง€ํ„ธ ํ† ํฐโ€์„ ๋งํ•œ๋‹ค. ๋ณ„๋„์˜ DB ์กฐํšŒ์—†์ด๋„ ์•„ ์ด ํ† ํฐ์„ ๊ฐ€์ง€๊ณ ์žˆ์œผ๋‹ˆ ์ด ์‚ฌ์šฉ์ž๋Š” ์ธ์ฆ ๋ฐ›์€ ์‚ฌ์šฉ์ž์•ผ ๋ผ๊ณ  ์•Œ ์ˆ˜ ์žˆ๋‹ค.

ํ•ต์‹ฌ ๊ตฌ์กฐ ์š”์•ฝ

2025-04-29-025748.png

์ฃผ์š” ๋™์ž‘ ์ž์„ธํžˆ..

GitHub ๋กœ๊ทธ์ธ ์‹œ์ž‘

  • /api/auth/github ํ˜ธ์ถœ 2025-04-29-014101.png
  • ์ง€์ •๋œ OAuth ์ œ๊ณต์ž์— ๋Œ€ํ•œ ์ธ์ฆ URL ๋ฐ˜ํ™˜ํ•ด์ค€๋‹ค -> ์—ฌ๊ธฐ์„  GITHUB 2025-04-28-010710.png
  • OAuth ์ œ๊ณต์ž๊ฐ€ ์šฐ์„  ์ง€๊ธˆ์€ github๋กœ๋งŒ ๋˜์–ด์žˆ๋Š”๋ฐ ๋” ์ถ”๊ฐ€๋  ์ˆ˜๋„ ์žˆ์–ด์„œ factory๋ฅผ ๋งŒ๋“ค์–ด์„œ ๋ฏธ๋ฆฌ ๋“ฑ๋ก์„ ์‹œ์ผœ๋‘๊ณ , ํ•„์š”ํ•œ๊ฑฐ ๊บผ๋‚ด์„œ ์“ฐ๋„๋ก ๊ตฌํ˜„์„ ํ–ˆ๋‹ค. 2025-04-29-013838.png
  • ๋กœ๊ทธ์ธ url์„ ์ƒ์„ฑํ•˜๋Š” ๊ณผ์ •. ์‚ฌ์šฉ์ž๊ฐ€ GitHub์— ๋กœ๊ทธ์ธํ•˜๋Ÿฌ ๊ฐˆ ์ˆ˜ ์žˆ๋„๋ก ์ ์ ˆํ•œ ์ฟผ๋ฆฌํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๋ถ™์ธ URL ์„ ๋งŒ๋“ค์–ด์ค€๋‹ค
  • .encode๋ฅผ ์ง€์ •ํ•ด์ฃผ์ง€ ์•Š์œผ๋ฉด URI.create() ํ•  ๋•Œ โ€œuser:email read:userโ€์ด๋ ‡๊ฒŒ ๊ทธ๋Œ€๋กœ ๋“ค์–ด๊ฐ€๊ฒŒ ๋ผ์„œ ์˜ค๋ฅ˜๊ฐ€ ๋‚œ๋‹ค. https://github.com/login/oauth/authorize?client_id=xxx&redirect_uri=xxx&scope=user:email%20read:user -> ์‚ฌ์šฉ์ž์˜ ์ด๋ฉ”์ผ์ด๋ž‘ ๊ธฐ๋ณธ ์‚ฌ์šฉ์ž ์ •๋ณด๋งŒ ์ฝ์–ด์˜ค๋Š”

์ด์ œ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ๋˜์„œ github OAuth๋กœ ์ด๋™ํ•จ

GitHub ์ฝœ๋ฐฑ ์ˆ˜์‹ 

  • ์‚ฌ์šฉ์ž๊ฐ€ ๋กœ๊ทธ์ธ ์„ฑ๊ณตํ•˜๋ฉด code๊ฐ€ ๋‹ด๊ฒจ์„œ /api/auth/github/callback?code=xxx๋กœ ์ด๋™ํ•œ๋‹ค.
  • ์„œ๋ฒ„๊ฐ€ ์ด ์ฝ”๋“œ๋ฅผ ์ด์šฉํ•ด์„œ
    • AccessToken ์š”์ฒญ
    • ์‚ฌ์šฉ์ž ์ •๋ณด ์กฐํšŒ
    • DB์— ์‚ฌ์šฉ์ž ์ €์žฅ ๊ทธ๋ฆฌ๊ณ  JWT ํ† ํฐ์„ ๋ฐœ๊ธ‰ํ•ด์„œ ํด๋ผ์ด์–ธํŠธ์— ์ „๋‹ฌํ•œ๋‹ค.

2025-04-28-013017.png 2025-04-28-013123.png

  • ์ธ์ฆ ์ฝ”๋“œ๋ฅผ github์—๊ฒŒ post ์š”์ฒญ์„ ๋ณด๋‚ด์„œ ์•„๊นŒ ๋ฐ›์€ code๋ฅผ access_token์œผ๋กœ ๋ฐ”๊พผ๋‹ค.
  • ์ด์ œ access token์„ Authorization ํ—ค๋”์— ๋„ฃ๊ณ  github api ํ˜ธ์ถœํ•ด์„œ ๋กœ๊ทธ์ธํ•œ ์‚ฌ์šฉ์ž์˜ ์ •๋ณด๋ฅผ ๋ฐ›์•„์˜จ๋‹ค.
    1
    2
    3
    4
    5
    6
    7
    {
    "id": 123456,
    "name": "ํ™๊ธธ๋™",
    "login": "hong",
    "email": "hong@example.com",
    "avatar_url": "https://avatars.githubusercontent.com/u/123456?v=4"
    }
    
  • ๊ทธ๋‹ค์Œ ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ Member๋กœ ๋ณ€ํ™˜ํ•œ๋‹ค. -> DB์— ์‚ฌ์šฉ์ž ์ •๋ณด ์ €์žฅ
  • ์ด์ œ ์ธ์ฆ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•œ๋‹ค. 2025-04-28-014617.png
    • Spring Security๊ฐ€ ์š”๊ตฌํ•˜๋Š” ํ‘œ์ค€ ์‚ฌ์šฉ์ž ์ •๋ณด ํฌ๋งท์„ ๋งž์ถฐ์„œ Spring Security๊ฐ€ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋„๋ก ๋ณ€ํ™˜ํ•œ๋‹ค. => โ€œ์ด ์‚ฌ๋žŒ์€ provierUid๋ฅผ ์•„์ด๋””๋กœ ๊ฐ€์ง„ ์‚ฌ์šฉ์ž์ด๊ณ , ์—ญํ• ์€ ROLE_XXX์•ผโ€
    • ์ด์ œ ์ด ์‚ฌ์šฉ์ž๊ฐ€ ๋ˆ„๊ตฐ์ง€, ์–ด๋–ค ๊ถŒํ•œ์„ ๊ฐ€์กŒ๋Š”์ง€ Spring Security๊ฐ€ ์•Œ ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๋‹ค.
  • ๊ทธ๋‹ค์Œ JWT ํ† ํฐ์„ ์ƒ์„ฑํ•˜๊ณ  TokenDto๋กœ ๋ฌถ์–ด์„œ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
    • TokenDto์—๋Š” accessToken(์•ก์„ธ์Šค ํ† ํฐ), refreshToken(๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ), tokenType(Bearer ์ผ๋ฐ˜์ ์œผ๋กœ), expriresln(๋งŒ๋ฃŒ์‹œ๊ฐ– (์ดˆ))

API ์š”์ฒญ์‹œ ์ธ์ฆ ์ฒ˜๋ฆฌ

  • ์ด์ œ ํด๋ผ์ด์–ธํŠธ๋Š” ์ดํ›„ ๋ชจ๋“  API ์š”์ฒญ์— Authorization: Bearer {accessToken} ํ—ค๋”๋ฅผ ๋ถ™์—ฌ์„œ ๋ณด๋‚ธ๋‹ค.(๋กœ๊ทธ์ธ ์•ˆํ•œ ์‚ฌ์šฉ์ž๋„ ๋ณผ ์ˆ˜ ์žˆ๋Š” ๊ฒฝ๋กœ๋Š” ์ œ์™ธํ•˜๊ณ )
  • JwtAuthenticationFilter ๊ฐ€ ์ด ํ† ํฐ์„ ์ฝ์–ด์„œ
    • ์œ ํšจํ•œ์ง€ ๊ฒ€์ฆ
    • ์‚ฌ์šฉ์ž ์ธ์ฆ(Authentication ๊ฐ์ฒด ์ƒ์„ฑ)ํ•ด์„œ SecurityContextHolder์— ์ €์žฅ
  • ๊ทธ๋ž˜์„œ ์ดํ›„ ์„œ๋น„์Šค ๋กœ์ง์—์„œ ๋ˆ„๊ฐ€ ์š”์ฒญํ–ˆ๋Š”์ง€ ๋ฐ”๋กœ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

JWTAuthenticatinFilter

  • ๋ชจ๋“  HTTP ์š”์ฒญ์€ ์ด ํ•„ํ„ฐ๋ฅผ ์ง€๋‚˜๊ฐ„๋‹ค. 2025-04-28-021027.png 2025-04-28-020820.png
    • Authorization ํ—ค๋”๊ฐ€ ์—†๊ฑฐ๋‚˜ Bearer ๋กœ ์‹œ์ž‘ํ•˜์ง€ ์•Š์œผ๋ฉด null๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.(์ธ์ฆ ์‹œ๋„ ์ž์ฒด๋ฅผ ์•ˆํ•œ๋‹ค)
    • Bearer eyJhbGciOiJIUzUxMiJ9... -> eyJhbGciOiJIUzUxMiJ9โ€ฆ ์ด ํ† ํฐ๋งŒ ์ถ”์ถœํ•œ๋‹ค.
  • ์œ ํšจํ•œ ํ† ํฐ์ด๋ฉด 2025-04-28-021138.png
    • JWT๋ฅผ ํŒŒ์‹ฑํ•ด์„œ Claims(=์•ˆ์— ๋‹ด๊ธด ๋ฐ์ดํ„ฐ)๋ฅผ ๊บผ๋‚ด๊ณ 
    • Claims์—์„œ ๊ถŒํ•œ ์ •๋ณด๋ฅผ ๊บผ๋‚ด๊ณ 
    • Claims์—์„œ ์‚ฌ์šฉ์ž ID๋ฅผ ๊บผ๋‚ด๊ณ 
    • UserDetails ๊ฐ์ฒด ์ƒ์„ฑํ•˜๊ณ 
    • UsernamePasswordAuthenticationToken์œผ๋กœ ๊ฐ์‹ธ์„œ ๋ฐ˜ํ™˜์„ ํ•œ๋‹ค.
  • ์ธ์ฆ ์ •๋ณด ์ €์žฅ์„ ํ•œ๋‹ค.(SecurityContextHolder.getContext().setAuthentication(authentication))
    • SpringSecurity๋Š” ์š”์ฒญ ํ•˜๋‚˜ํ•˜๋‚˜๋งˆ๋‹ค SecurityContext๋ฅผ ๋“ค๊ณ  ๊ด€๋ฆฌ๋ฅผ ํ•˜๋Š”๋ฐ ์—ฌ๊ธฐ๋‹ค๊ฐ€ โ€œ์ด ์š”์ฒญ์€ ์–ด๋–ค ์‚ฌ์šฉ์ž๊ฐ€ ๋ณด๋ƒˆ๋‹คโ€๋Š” ์ •๋ณด๋ฅผ ์ €์žฅ์„ ํ•œ๋‹ค.
    • ์ดํ›„์— ์„œ๋น„์Šค ๋ ˆ์ด์–ด ์—์„œ๋Š” ํ˜„์žฌ ๋กœ๊ทธ์ธํ•œ ์‚ฌ์šฉ์ž๋ฅผ ๊บผ๋‚ด์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค.
  • ์ด์ œ ์ธ์ฆ์ด ์„ฑ๊ณตํ•œ ํ›„์— ํ•„ํ„ฐ ์ฒด์ธ ๋‹ค์Œ๋‹จ๊ณ„๋กœ ๋„˜์–ด๊ฐ€๋ฉด์„œ ์ปจํŠธ๋กค๋Ÿฌ์— ์ง„์ž…ํ•œ๋‹ค.

@Authenticated ์‚ฌ์šฉ(์ปค์Šคํ…€ Annotation)

  • ํŠน์ • Api์—์„œ ๋Š” @Authenticated๋ฅผ ๋ถ™์—ฌ์„œ Authrization ํ—ค๋”์˜ JWT ์•ˆ์— ์žˆ๋Š” ์‚ฌ์šฉ์ž ID๋ฅผ ๋ฐ”๋กœ ๊บผ๋‚ด์„œ ์“ธ ์ˆ˜ ์žˆ๊ฒŒ ํ–ˆ๋‹ค.
  • ์ธ์ฆ์ •๋ณด๋ฅผ ์ฃผ์ž… ๋ฐ›์„ ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—‡๋‹ค. 2025-04-28-021924.png