[Security] Security-basic

Updated:

Categories:

Tags: , ,

๐Ÿ“Œ ๊ฐœ์ธ์ ์ธ ๊ณต๊ฐ„์œผ๋กœ ๊ณต๋ถ€๋ฅผ ๊ธฐ๋กํ•˜๊ณ  ๋ณต์Šตํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•˜๋Š” ๋ธ”๋กœ๊ทธ์ž…๋‹ˆ๋‹ค.
์ •ํ™•ํ•˜์ง€ ์•Š์€ ์ •๋ณด๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ์œผ๋‹ˆ ์ฐธ๊ณ ๋ฐ”๋ž๋‹ˆ๋‹ค :๐Ÿ˜ธ
[ํ‹€๋ฆฐ ๋‚ด์šฉ์€ ๋Œ“๊ธ€๋กœ ๋‚จ๊ฒจ์ฃผ์‹œ๋ฉด ๋ณต๋ฐ›์œผ์‹ค๊ฑฐ์—์š”]

Spring Security ๋ž€?

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— Security๋ฅผ ์ ์šฉํ•˜์ง€ ์•Š์œผ๋ฉด

  1. ๋กœ๊ทธ์ธ ๊ธฐ๋Šฅ (์ธ์ฆ,Authentication)์ด ์—†์Œ.
  2. API ์— ๋Œ€ํ•œ ๊ถŒํ•œ ๋ถ€์—ฌ(์ธ๊ฐ€, Athorization)๊ธฐ๋Šฅ์ด ์—†์Œ.

    ์ธ์ฆ๊ณผ ์ธ๊ฐ€ ์ค‘์— ๋ญ๊ฐ€ ๋จผ์ € ์ง„ํ–‰? โ†’ ์ธ์ฆ์ด ๋จผ์ € โžก๏ธ ๊ทธ๋‹ค์Œ ์ธ๊ฐ€์ฒ˜๋ฆฌ๋ฅผ ํ•˜๊ณ  ๋ณด๋ƒ„

  3. ์›น ๋ณด์•ˆ ์ทจ์•ฝ์ ์— ๋Œ€ํ•œ ๋Œ€๋น„๊ฐ€ ์ „ํ˜€ ์ด๋ฃจ์–ด์ง€์ง€ ์•Š์•˜์Œ,
    ์ฆ‰, ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์œ„ํ˜‘ํ•˜๋Š” ์„ธ์…˜ ๊ณ ์ • ๊ณต๊ฒฉ, ํด๋ฆญ์žฌํ‚น ๊ณต๊ฒฉ, CSRF ๋“ฑ์˜ ๋ณด์•ˆ ์ทจ์•ฝ์ ์— ๋Œ€ํ•œ
    ๊ณ ๋ ค๊ฐ€ ์ „ํ˜€ ์ด๋ฃจ์–ด์ง€์ง€ ์•Š์€ ์ƒํƒœ์ด๋‹ค.

โœ๏ธ Spring Security

Spring MVC ๊ธฐ๋ฐ˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ธ์ฆ(Authentication)๊ณผ ์ธ๊ฐ€(Authorization or ๊ถŒํ•œ ๋ถ€์—ฌ) ๊ธฐ๋Šฅ์„ ์ง€์›ํ•˜๋Š” ๋ณด์•ˆ ํ”„๋ ˆ์ž„์›Œํฌ๋กœ์จ,</br> Spring MVC ๊ธฐ๋ฐ˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ๋ณด์•ˆ์„ ์ ์šฉํ•˜๊ธฐ ์œ„ํ•œ ์‚ฌ์‹ค์ƒ์˜ ํ‘œ์ค€

Spring์—์„œ ์ง€์›ํ•˜๋Š” Interceptor๋‚˜ Servlet Filter๋ฅผ ์ด์šฉํ•ด์„œ ๋ณด์•ˆ ๊ธฐ๋Šฅ์„ ์ง์ ‘ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ
์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ณด์•ˆ์„ ๋Œ€๋ถ€๋ถ„์˜ ๊ธฐ๋Šฅ์„ Spring Security์—์„œ ์•ˆ์ •์ ์œผ๋กœ ์ง€์›ํ•˜๊ณ  ์žˆ์œผ๋ฏ€๋กœ
๊ตฌ์กฐ์ ์œผ๋กœ ์ž˜ ๋งŒ๋“ค์–ด์ง„ ๊ฒ€์ฆ๋œ Spring Security๋ฅผ ์ด์šฉํ•˜๋Š” ๊ฒƒ์ด ์•ˆ์ „ํ•œ ์„ ํƒ

โœ๏ธ Interceptor์™€ Filter

  • interceptor๋Š” ์ปจํŠธ๋กค๋Ÿฌ ๋ฉ”์„œ๋“œ๋กœ ๋„˜์–ด๊ฐ€๊ธฐ ์ง์ „์— ๊ฐ€๋กœ์ฑ„์„œ ์ฒ˜๋ฆฌํ•˜๊ณ  ๋ณด๋‚ด์คŒ. (๋ณด์™„๊ด€๋ จ ์ด์Šˆ์ฒ˜๋ฆฌ, ์ธ์ฆ-์ธ๊ฐ€)
  • ๊ทธ๋Ÿฐ๋ฐ interceptor ์‚ฌ์šฉ ์•ˆํ•˜๊ณ  filter๋ฅผ ์‚ฌ์šฉํ•  ๊ฒƒ.
    (์™œ๋ƒ๋ฉด ๋ณด์•ˆ์€ ์ดˆ๊ธฐ ๋‹จ๊ณ„์—์„œ ์š”์ฒญ๋˜์•ผ ํ•˜๋Š”๋ฐ filter ๊ฐ€ ๋” ์ผ์ฐ ์š”์ฒญ์„ ๊ฐ€๋กœ์ฑ„์–ด์„œ ์ฒ˜๋ฆฌ ํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.-์ „์—ญ์—์„œ ์ ์šฉ๊ฐ€๋Šฅ)
  • filter๋Š” ๋ธŒ๋ผ์šฐ์ €์—์„œ ๋””์ŠคํŒจ์ณ ์„œ๋ธ”๋ฆฟ์œผ๋กœ ๋„˜์–ด๊ฐ€๊ธฐ ์ง์ „์— ์ฒ˜๋ฆฌํ•œ๋‹ค.
  • ๋ณด์•ˆ ๊ด€๋ จ ์ž‘์—…์„ ์œ„ํ•ด Spring Security์—์„œ๋Š” ์ฃผ๋กœ Filter๊ธฐ๋ฐ˜์œผ๋กœ ์‚ฌ์šฉํ•œ๋‹ค.
  • Spring Security์˜ ํ•„ํ„ฐ๊ฐ€ ๊ณผ๊ฑฐ์—๋Š” DispatcherServlet ์ „์— ๋™์ž‘ํ•˜๋ฉด์„œ Spring ์ปจํ…Œ์ด๋„ˆ์˜ ๋นˆ๋“ค์„ ์ง์ ‘ ํ™œ์šฉํ•˜๊ธฐ ์–ด๋ ค์› ์ง€๋งŒ, ํ˜„์žฌ๋Š” ์ด๋Ÿฌํ•œ ์ œ์•ฝ์ด ํ•ด๊ฒฐ๋˜์–ด ํ•„ํ„ฐ๋„ Spring์˜ ๋นˆ์„ ์ฃผ์ž…๋ฐ›์•„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๋‹ค.
  • 6๋ฒ„์ „ ๋ถ€ํ„ฐ๋Š” ๋žŒ๋‹ค์‹์„ ์‚ฌ์šฉ , ์•„์ง 6๋ฒ„์ „์„ ์‚ฌ์šฉํ•˜๋Š” ๊ณณ์ด ๋งŽ์ง€ ์•Š์•„์„œ 5.8๋ฒ„์ „์œผ๋กœ ๊ณต๋ถ€ํ•  ์˜ˆ์ •.

โœ๏ธ Spring Security๋กœ ํ•  ์ˆ˜ ์žˆ๋Š” ๋ณด์•ˆ ๊ฐ•ํ™” ๊ธฐ๋Šฅ

Spring Security๋ฅผ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ์ ์šฉํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ผ๋“ค์„ ํ•  ์ˆ˜ ์žˆ๋‹ค.

  • ๋‹ค์–‘ํ•œ ์œ ํ˜•(ํผ ๋กœ๊ทธ์ธ ์ธ์ฆ, ํ† ํฐ ๊ธฐ๋ฐ˜ ์ธ์ฆ, OAuth 2 ๊ธฐ๋ฐ˜ ์ธ์ฆ, LDAP ์ธ์ฆ)์˜ ์‚ฌ์šฉ์ž ์ธ์ฆ ๊ธฐ๋Šฅ ์ ์šฉ
  • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‚ฌ์šฉ์ž์˜ ์—ญํ• (Role)์— ๋”ฐ๋ฅธ ๊ถŒํ•œ ๋ ˆ๋ฒจ ์ ์šฉ
  • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์ œ๊ณตํ•˜๋Š” ๋ฆฌ์†Œ์Šค์— ๋Œ€ํ•œ ์ ‘๊ทผ ์ œ์–ด
  • ๋ฏผ๊ฐํ•œ ์ •๋ณด์— ๋Œ€ํ•œ ๋ฐ์ดํ„ฐ ์•”ํ˜ธํ™”
  • SSL ์ ์šฉ
  • ์ผ๋ฐ˜์ ์œผ๋กœ ์•Œ๋ ค์ง„ ์›น ๋ณด์•ˆ ๊ณต๊ฒฉ ์ฐจ๋‹จ

์ด ์™ธ์—๋„ SSO, ํด๋ผ์ด์–ธํŠธ ์ธ์ฆ์„œ ๊ธฐ๋ฐ˜ ์ธ์ฆ, ๋ฉ”์„œ๋“œ ๋ณด์•ˆ, ์ ‘๊ทผ ์ œ์–ด ๋ชฉ๋ก(Access Control List) ๊ฐ™์€ ๋ณด์•ˆ์„ ์œ„ํ•œ ๊ธฐ๋Šฅ๋“ค์„ ์ง€์›ํ•œ๋‹ค.

โœ๏ธ Spring Security์—์„œ ์‚ฌ์šฉํ•˜๋Š” ์šฉ์–ด ์ •๋ฆฌ

  • Principal(์ฃผ์ฒด) โ†’ ๋ˆ„๊ฐ€(who?)๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋จ.
    • Spring Security์—์„œ ์‚ฌ์šฉ๋˜๋Š” Principal์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ์‚ฌ์šฉ์ž, ๋””๋ฐ”์ด์Šค ๋˜๋Š” ์‹œ์Šคํ…œ ๋“ฑ์ด ๋  ์ˆ˜ ์žˆ์œผ๋ฉฐ,
      ์ผ๋ฐ˜์ ์œผ๋กœ ์ธ์ฆ ํ”„๋กœ์„ธ์Šค๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ ์ˆ˜ํ–‰๋œ ์‚ฌ์šฉ์ž์˜ ๊ณ„์ • ์ •๋ณด๋ฅผ ์˜๋ฏธํ•œ๋‹ค.
    • ์‹ ์› ์ฆ๋ช…์ด๋ž‘์€ ๋ณ„๊ฐœ โ‡’ ์ด๊ฑด credential์ด๋ผ๊ณ  ํ•จ .
  • Authentication(์ธ์ฆ)
    • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์‚ฌ์šฉํ•˜๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ๋ณธ์ธ์ด ๋งž์Œ์„ ์ฆ๋ช…ํ•˜๋Š” ์ ˆ์ฐจ๋ฅผ ์˜๋ฏธ
    • ์ •์ƒ์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์‚ฌ์šฉ์ž๋ฅผ ์‹๋ณ„ํ•˜๊ธฐ ์œ„ํ•œ ์ •๋ณด๊ฐ€ ํ•„์š”ํ•œ๋ฐ ์ด๋ฅผ Credential(์‹ ์› ์ฆ๋ช… ์ •๋ณด)์ด๋ผ๊ณ  ํ•œ๋‹ค.
    • Credential ์€ ์ผ์ƒ์—์„œ ๋Œ€ํ‘œ์ ์ธ ์˜ˆ๋กœ ์ฃผ๋ฏผ๋“ฑ๋ก์ฆ, ๊ทธ๋ฆฌ๊ณ  ํŠน์ • ์‚ฌ์ดํŠธ์—์„œ ๋กœ๊ทธ์ธ์„ ์œ„ํ•ด ์ž…๋ ฅํ•˜๋Š” ํŒจ์Šค์›Œ๋“œ๋„ ๋กœ๊ทธ์ธ ์•„์ด๋””๋ฅผ ์ฆ๋ช…ํ•˜๊ธฐ์œ„ํ•œ Credential์ด๋‹ค.
  • Authorization(์ธ๊ฐ€ ๋˜๋Š” ๊ถŒํ•œ ๋ถ€์—ฌ)
    • Authentication์ด ์ •์ƒ์ ์œผ๋กœ ์ˆ˜ํ–‰๋œ ์‚ฌ์šฉ์ž์—๊ฒŒ ํ•˜๋‚˜ ์ด์ƒ์˜ ๊ถŒํ•œ(authority)์„ ๋ถ€์—ฌํ•˜์—ฌ
      ํŠน์ • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ํŠน์ • ๋ฆฌ์†Œ์Šค์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ—ˆ๊ฐ€ํ•˜๋Š” ๊ณผ์ •์„ ์˜๋ฏธํ•œ๋‹ค.
    • ๋ฐ˜๋“œ์‹œ Authentication ๊ณผ์ • ์ดํ›„ ์ˆ˜ํ–‰๋˜์–ด์•ผ ํ•˜๋ฉฐ ๊ถŒํ•œ์€ ์ผ๋ฐ˜์ ์œผ๋กœ ์—ญํ• (Role) ํ˜•ํƒœ๋กœ ๋ถ€์—ฌ๋œ๋‹ค.
  • Access Control(์ ‘๊ทผ ์ œ์–ด)
    • ์‚ฌ์šฉ์ž๊ฐ€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋ฆฌ์†Œ์Šค์— ์ ‘๊ทผํ•˜๋Š” ํ–‰์œ„๋ฅผ ์ œ์–ดํ•˜๋Š” ๊ฒƒ์„ ์˜๋ฏธ

โœ๏ธ Spring Security๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋Š” ์ด์œ 

  • ๊ฐ€์žฅ ํฐ ์ด์œ ๋Š” ๋ณด์•ˆ์„ ๊ฐ•ํ™”ํ•˜๊ธฐ ์œ„ํ•œ ์†”๋ฃจ์…˜์œผ๋กœ Spring Security ๋งŒ ํ•œ ๋‹ค๋ฅธ ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ
    Spring Security๊ฐ€ ๋‘ ์„ธ๊ฐ€์ง€ ๋ณด์•ˆ ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ๋ถ™์ธ ๊ฒƒ๋ณด๋‹ค ๋” ๋‚˜์€ ๊ฒฝ์šฐ๊ฐ€ ๋Œ€๋‹ค์ˆ˜.
  • Spring Security๋Š” ํŠน์ • ๋ณด์•ˆ ์š”๊ตฌ ์‚ฌํ•ญ์„ ๋งŒ์กฑํ•˜๊ธฐ ์œ„ํ•œ ์ปค์Šคํ„ฐ๋งˆ์ด์ง•์ด ์šฉ์ดํ•˜๊ณ , ์œ ์—ฐํ•œ ํ™•์žฅ์ด ๊ฐ€๋Šฅํ•˜๋‹ค. โ†’ ์ปค์Šคํ„ฐ๋งˆ์ด์ง•์ด ์–ด๋ ค์šด ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์Œ.


Spring Security์˜ ์ธ์ฆ์ฒ˜๋ฆฌ ํ๋ฆ„

SSR๋กœ ๊ตฌํ˜„, ์„œ์„œํžˆ ๊ฐœ์„ ํ•ด ๋‚˜๊ฐˆ ์˜ˆ์ •

โœ๏ธ SSR๊ณผ CSR

  1. SSR(Server Side Rendering) ๋ฐฉ์‹์˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ์„ธ์…˜ ๊ธฐ๋ฐ˜์˜ ํผ ๋กœ๊ทธ์ธ ๋ฐฉ์‹์„ ์ ์šฉํ•˜๊ธฐ์—
    ๊ฐ€์žฅ ์ ํ•ฉํ•œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด๋ฉฐ,
    ๋˜ํ•œ ํผ ๋กœ๊ทธ์ธ ๋ฐฉ์‹์€ Spring Security์— ์ฒ˜์Œ ์ž…๋ฌธํ•˜๋Š” ์‚ฌ๋žŒ๋“ค์ด ์ดํ•ดํ•˜๊ธฐ์— ๊ฐ€์žฅ ์ ํ•ฉํ•œ ์ธ์ฆ ๋ฐฉ์‹
    โญ CSR(Client Side Rendering) ๋ฐฉ์‹์œผ๋กœ ์ด๋•Œ๋™์•ˆ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ตฌํ˜„ ํ•ด์™”์ง€๋งŒ,
    Spring Security๋ฅผ ์ž…๋ฌธํ•˜๊ธฐ ์œ„ํ•ด SSR ๋ฐฉ์‹ ์‚ฌ์šฉ

โœ๏ธ Hello Spring Security ์ƒ˜ํ”Œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๊ตฌ์กฐ

HTML ๋ทฐ๋ฅผ ๊ตฌ์„ฑํ•˜๊ธฐ ์œ„ํ•ด ํƒ€์ž„๋ฆฌํ”„(Thymeleaf)๋ผ๋Š” ํ…œํ”Œ๋ฆฟ ์—”์ง„์„ ์‚ฌ์šฉ

  1. Header

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    
     <html xmlns:th="<http://www.thymeleaf.org>"
           xmlns:sec="<http://www.thymeleaf.org/thymeleaf-extras-springsecurity5>"> <!-- (1) -->
     <body>
     <div align="right" th:fragment="header">
         <a href="/members/register" class="text-decoration-none">ํšŒ์›๊ฐ€์ž…</a> |
         <span sec:authorize="isAuthenticated()"> <!-- (2) ์ธ์ฆ์ด ๋˜์—ˆ์œผ๋ฉด ๋ณด์—ฌ์ง€๋Š” ๊ฒƒ๋“ค. -->
                     <span sec:authorize="hasRole('USER')">  <!-- (3) -->
                         <a href="/members/my-page" class="text-decoration-none">๋งˆ์ดํŽ˜์ด์ง€</a> |
                     </span>
                     <a href="/logout" class="text-decoration-none">๋กœ๊ทธ์•„์›ƒ</a>  <!-- (4) -->
                     <span th:text="${#authentication.name}">ํ™๊ธธ๋™</span>๋‹˜  <!-- (5) -->
                 </span>
        
         <span sec:authorize="!isAuthenticated()"> <!-- (6) ์ธ์ฆ์ด ๋˜์ง€ ์•Š์•˜์œผ๋ฉด -->
                     <a href="/auths/login-form" class="text-decoration-none">๋กœ๊ทธ์ธ</a>
                 </span>
     </div>
     </body>
     </html>
    
  2. ํšŒ์›๊ฐ€์ž… HTML

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    
     <!DOCTYPE html>
     <html xmlns:th="<http://www.thymeleaf.org>"
           xmlns:layout="<http://www.ultraq.net.nz/thymeleaf/layout>"
           layout:decorate="layouts/common-layout">
     <head>
         <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
         <title>Hello Spring Security Coffee Shop</title>
     </head>
     <body>
         <hr />
         <div class="container" layout:fragment="content">
             <!-- (1) ํšŒ์› ๊ฐ€์ž… ํผ -->
             <form action="/members/register" method="post">
                 <div class="row">
                     <div class="col-xs-2">
                         <input type="text" name="fullName" class="form-control" placeholder="User Name"/>
                     </div>
                 </div>
                 <div class="row" style="margin-top: 20px">
                     <div class="col-xs-2">
                         <input type="email" name="email" class="form-control" placeholder="Email"/>
                     </div>
                 </div>
                 <div class="row" style="margin-top: 20px">
                     <div class="col-xs-2">
                         <input type="password" name="password" class="form-control" placeholder="Password"/>
                     </div>
                 </div>
        
                 <button class="btn btn-outline-secondary" style="margin-top: 20px">ํšŒ์› ๊ฐ€์ž…</button>
             </form>
         </div>
     </body>
     </html>
        
    
  3. ๋กœ๊ทธ์ธ

    ๋กœ๊ทธ์ธ ํ™”๋ฉด ์—ญ์‹œ HTML form ํƒœ๊ทธ๋กœ ๊ตฌ์„ฑ์ด ๋˜์–ด ์žˆ์œผ๋ฉฐ,
    HTML์˜ form ๋ฐฉ์‹์œผ๋กœ ๋กœ๊ทธ์ธ ์ธ์ฆ์„ ์ง„ํ–‰ํ•˜๊ธฐ ๋•Œ๋ฌธ์—
    Spring Security์—์„œ๋„ ์ด๋Ÿฌํ•œ ์ธ์ฆ ๋ฐฉ์‹์„ ํผ ๋กœ๊ทธ์ธ ์ธ์ฆ

  4. ์„œ๋ธŒ์‚ฌ์ด๋“œ๋ Œ๋”๋ง ๋ฐฉ์‹์ด๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ์ฒด๋ฅผ ๋ณด๋‚ด์ง€ ์•Š๋Š”๋‹ค.

๋ฌธ์ œ์ 

  • ๋กœ๊ทธ์ธ ๊ธฐ๋Šฅ์ด ๊ตฌ์ฒด์ ์œผ๋กœ ๊ตฌํ˜„๋˜์–ด ์žˆ์ง€ ์•Š์Œ. โ†’ ์ธ์ฆ, ์ธ๊ฐ€๋ฅผ ๊ตฌํ˜„ํ•ด์•ผ ํ•จ,

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    
      package com.springboot.auth;
        
      import org.springframework.stereotype.Controller;
      import org.springframework.web.bind.annotation.GetMapping;
      import org.springframework.web.bind.annotation.PostMapping;
      import org.springframework.web.bind.annotation.RequestMapping;
        
      @Controller
      @RequestMapping("/auths")
      public class AuthController {
          @GetMapping("/login-form")
          public String loginForm() {
              return "login";
          }
        
          @GetMapping("/access-denied")
          public String accessDenied() {
              return "access-denied";
          }
        
          // (1)
          @PostMapping("/login")
          public String login() {
              System.out.println("Login successfully!");
              return "home";
          }
      }
        
    

    ๋กœ๊ทธ์ธ ์ธ์ฆ์ด ์ •์ƒ์ ์œผ๋กœ ์ด๋ฃจ์–ด์ง€์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์ปคํ”ผ๋ฅผ ์ฃผ๋ฌธํ•˜๋Š” ํšŒ์›์ด๋“ , ๋งค์žฅ์—์„œ ์ปคํ”ผ๋ฅผ ๋งŒ๋“ค์–ด์„œ ์ œ๊ณตํ•˜๋Š” ์นดํŽ˜ ๊ด€๊ณ„์ž์ด๋“  ์ƒ๊ด€์—†์ด ๋ชจ๋“  ํ™”๋ฉด์— ์ž์œ ๋กญ๊ฒŒ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค.

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— Spring Security ์ ์šฉ

  1. gradle์— ์ฃผ์„์ฒ˜๋ฆฌ ๋˜์–ด์žˆ๋Š” ์ธ์ฆ์ธ๊ฐ€๋ฅผ ํ•ด์ œํ•˜์—ฌ ์˜์กด์ฃผ์ž…ํ•œ๋‹ค.

  • ์›๋ž˜ HomeController์—์„œ ๊ธฐ๋ณธ url๋กœ ์ด๋™์‹œ home์œผ๋กœ ๊ฐ€๊ฒŒ ์„ค์ •๋˜์–ด์žˆ๋Š”๋ฐ
    ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ๋ฅผ ์ ์šฉํ•˜๋ฉด ๊ธฐ๋ณธ์œผ๋กœ ์ œ๊ณตํ•ด์ฃผ๋Š” ๊ธฐ๋Šฅ์€ ์ฒซ ํ™ˆ ํ™”๋ฉด์ด ๋กœ๊ทธ์ธ ํ™”๋ฉด์œผ๋กœ ๋ฐ”๋€๋‹ค.

  • ๋กœ๊ทธ์ธํ•  ๋•Œ ์•„์ด๋””๋Š” ๊ธฐ๋ณธ์œผ๋กœ user์ด๊ณ  ๋น„๋ฐ€๋ฒˆํ˜ธ๋Š” ํ”„๋กœ๊ทธ๋žจ ์‹คํ–‰์‹œ ์ œ๊ณต๋œ๋‹ค(๋งค๋ฒˆ ๋‹ฌ๋ผ์ง)

  • ๋‚ด ๋งˆ์Œ๋Œ€๋กœ ์•„์ด๋””์™€ ๋น„๋ฐ€ ๋ฒˆํ˜ธ๋ฅผ ์ ์œผ๋ฉด ์‹คํŒจ๊นŒ์ง€ ์•Œ๋ ค์ค€๋‹ค.

  • ์‹ค๋ฌด์— ์ ์šฉํ•˜๊ธฐ ํž˜๋“ฌ โ†’ ์ ‘์†ํ•  ๋•Œ๋งˆ๋‹ค ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ๋ฐ”๋€Œ๊ฒŒ ๋˜๊ณ , ๋กœ๊ทธ์ธ ํ•ด์„œ ๋“ค์–ด๊ฐ€๋ฉด ๋กœ๊ทธ์ธ ํ™”๋ฉด์ด ๋˜ ์žˆ๋‹ค(๋‚ด๊ฐ€ ๊ตฌํ˜„ํ•œ ๊ฒƒ)
  • ์Šคํ”„๋ง์—์„œ ๊ธฐ๋ณธ์œผ๋กœ ์ œ๊ณตํ•ด์ฃผ๋Š” ์ฒซ ํ™”๋ฉด์˜ ๋กœ๊ทธ์ธํ™”๋ฉด๊ณผ ๋‚ด๊ฐ€ ๊ตฌํ˜„ํ•œ ๋กœ๊ทธ์ธ ํ™”๋ฉด ๋‘ ๊ฐœ๊ฐ€ ์ค‘๋ณตํ•ด์„œ ์ƒ๊ธฐ๊ฒŒ ๋จ.

1๏ธโƒฃ ๋น„๋ฐ€๋ฒˆํ˜ธ ์„ค์ • ๋ณ€๊ฒฝ

โญ Spring Security Configuration ์ ์šฉ โ†’ ํด๋ž˜์Šค์—์„œ Spring Security์—์„œ ์ง€์›ํ•˜๋Š” ์ธ์ฆ๊ณผ ๊ถŒํ•œ ๋ถ€์—ฌ ์„ค์ •ํ•˜๊ธฐ

  • ํ”„๋กœ๊ทธ๋žจ ์„ค์ •์‹œ ์ƒ์„ฑ๋˜๋Š” ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ์•„๋‹Œ
  • ๋งค๋ฒˆ ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ๋ฐ”๋€Œ๋Š” ๊ฒƒ์„ ๋‹ค์‹œ ์„ค์ •ํ•  ์˜ˆ์ •, db๋Š” ์ธ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ

SecurityConfiguration

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.springboot.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.provisioning.UserDetailsManager;

@Configuration //Configuration๊ณผ ๋นˆ์ด ์žˆ์œผ๋ฉด ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฐ์ฒด๋ฅผ ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•˜๊ฒŒ ๋จ.
public class SecurityConfiguration {
    @Bean //์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ์˜ ๋„์›€์„ ๋ฐ›๊ธฐ ์œ„ํ•จ
    public UserDetailsManager userDetailService(){
        UserDetails userDetails =
                User.withDefaultPasswordEncoder()
                        .username("jerry@gmail.com") //์ผ๋ฐ˜์ ์œผ๋กœ ํ†ต์šฉ๋˜๋Š” ์•„์ด๋””์˜ ๊ฐœ๋….
                        .password("wpfl")
                        .roles("USER") //์ธ๊ฐ€ : ๊ถŒํ•œ์„ ๋ญ˜ ์ค„ ๊ฒƒ์ธ์ง€?
                        .build(); // 3๊ฐ€์ง€๋ฅผ ๋‹ด๊ณ  ๋นŒ๋“œํ•˜๋ฉด
        return new InMemoryUserDetailsManager(userDetails);
        //์œ ์ €๊ฐ์ฒด๊ฐ€ ๋‚˜์˜จ๋‹ค.
        // ๋ฉ”์„œ๋“œ๋ช…์„ ๋ณด๋ฉด UserDetalis๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ์—ญํ• ์„ ํ•˜๋Š” ๋งค๋‹ˆ์ €๋กœ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•˜๋Š”๋ฐ
        // ์ธ๋ฉ”๋ชจ๋ฆฌ ๋งค๋‹ˆ์ €๋กœ ๋ฐ˜ํ™˜ํ•จ. ์ธํ„ฐํŽ˜์ด์Šค๋กœ ๊ตฌํ˜„๋˜์–ด์žˆ๊ณ  ์ถ”์ƒํ™” ๋˜์–ด์žˆ๊ธฐ ๋•Œ๋ฌธ
        // ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์‹ค์ œ๋กœ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ธ๋ฉ”๋ชจ๋ฆฌDB์˜ ๊ตฌํ˜„์ฒด๋กœ ์‚ฌ์šฉ
        // ๋นˆ์€ userDetailService๋ผ๋Š” ๋ฉ”์„œ๋“œ ๋ช…์œผ๋กœ ๋“ฑ๋กํ•˜๊ฒŒ ๋œ๋‹ค.

    }
}

  1. UserDetails ์ธํ„ฐํŽ˜์ด์Šค๋Š” ์ธ์ฆ๋œ ์‚ฌ์šฉ์ž์˜ ํ•ต์‹ฌ ์ •๋ณด๋ฅผ ํฌํ•จํ•˜๊ณ  ์žˆ์œผ๋ฉฐ,
    UserDetails ๊ตฌํ˜„์ฒด์ธ User ํด๋ž˜์Šค๋ฅผ ์ด์šฉํ•ด์„œ ์‚ฌ์šฉ์ž์˜ ์ธ์ฆ ์ •๋ณด๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.
    • UserDetails

    • User

      • ์—ฌ๊ธฐ์„œ username์€ ์‚ฌ๋žŒ์˜ ์ด๋ฆ„์ด ์•„๋‹Œ ๊ณ ์œ ํ•œ ์‚ฌ์šฉ์ž๋ฅผ ์‹๋ณ„ํ•  ์ˆ˜ ์žˆ๋Š” ์‚ฌ์šฉ์ž ์•„์ด๋”” ๊ฐ™์€ ๊ฐ’

      • User class์— ์žˆ๋Š” static Method โ†’ withDefaultPasswordEncorder( )

        • withDefaultPasswordEncorder( ) ๋กœ ์ธํ•ด ์•”ํ˜ธํ™” ๋œ๋‹ค.

        API ๋ฌธ์„œ์—์„œ ํ•ด๋‹น ๋ฉ”์„œ๋“œ์—๋Š” ๋”์ด์ƒ ์‚ฌ์šฉํ•˜์ง€๋ง๋ผ๋Š” @Deprecated ์ด ๋‹ฌ๋ ค์žˆ์ง€๋งŒโ†’ ์‹ค์ œ ์„œ๋น„์Šคํ™˜๊ฒฝ์—์„œ๋Š” ์‚ฌ์šฉํ•˜์ง€ ๋ง๋ผ๋Š” ์˜๋ฏธ๋กœ ์• ๋„ˆํ…Œ์ด์…˜์ด ๋‹ฌ๋ ค์žˆ๋Š” ๊ฒƒ - ์ง€๊ธˆ ์—ฐ์Šต๋‹จ๊ณ„๋ผ์„œ ์˜๋ฏธ ๋‘˜ ํ•„์š” ์—†๊ณ  ํ•˜๋‚˜์”ฉ ๊ฐœ์„ ์˜ˆ์ •

  2. ๋ฐ˜ํ™˜์€ UserDetailsManager๋กœ ํ•ด์•ผํ•˜๋Š”๋ฐ, InMemoryUserDetailsManager๋กœ ๋ฐ˜ํ™˜ํ•จ.
    • ์ธํ„ฐํŽ˜์ด์Šค๋กœ ๊ตฌํ˜„๋˜์–ด์žˆ๊ณ  ์ถ”์ƒํ™” ๋˜์–ด์žˆ๊ธฐ ๋•Œ๋ฌธ

2๏ธโƒฃ ์ปค์Šคํ…€ ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€ ์‚ฌ์šฉ, ํผ๋กœ๊ทธ์ธ ๊ตฌํ˜„ (HTTP ๋ณด์•ˆ ๊ตฌ์„ฑ)

SecurityConfiguration์— filterChain ๋ฉ”์„œ๋“œ๋ฅผ ์ƒ์„ฑ
โ‡’ HttpSecurity๋ฅผ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๊ฐ€์ง€๊ณ , SecurityFilterChain์„ ๋ฆฌํ„ดํ•˜๋Š” ํ˜•ํƒœ์˜ ๋ฉ”์„œ๋“œ๋ฅผ ์ •์˜ํ•˜๋ฉด HTTP ๋ณด์•ˆ ์„ค์ •์„ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        //httpsecurity : ์›น๋ณด์•ˆ์„ ์œ„ํ•ด์„œ ์‚ฌ์šฉ๋˜๋Š” ํด๋ž˜์Šค
        //๋กœ๊ทธ์ธ์ด ๋˜ ์žˆ์œผ๋‹ˆ๊นŒ ๋‚ด๊ฐ€ ๊ตฌํ˜„ํ•œ ๋กœ๊ทธ์ธํŽ˜์ด์ง€๋กœ ์‚ฌ์šฉํ•˜๋„๋ก ํ•˜๊ฒŒ ๋ณ€๊ฒฝํ•˜๊ธฐ ์œ„ํ•ด์„œ ๋ฉ”์„œ๋“œ๋งŒ๋“ฌ
        //ํผ๋กœ๊ทธ์ธ์œผ๋กœ ๊ตฌํ˜„๋˜์–ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ํผ๋กœ๊ทธ์ธ์œผ๋กœ ์‚ฌ์šฉ์˜ˆ์ •
						
				//(1)		
        //ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์—์„œ DISABLE ์‚ฌ์šฉ ์ฒ˜๋ฆฌ, 
        // **CSRF(Cross-Site Request Forgery) ๊ณต๊ฒฉ์— ๋Œ€ํ•œ Spring Security์— ๋Œ€ํ•œ ์„ค์ •์„ ๋น„ํ™œ์„ฑํ™”**
        http.csrf().disable() 
                .formLogin() //(2) 
                .loginPage("/auths/login-form") //(3)
                //๋กœ๊ทธ์ธํŽ˜์ด์ง€ ์‚ฌ์šฉํ•˜๋Š” ์„ค์ •
                .loginProcessingUrl("process_login")
                //๋กœ๊ทธ์ธ์„ ์‹ค์ œ๋กœ ์ˆ˜ํ–‰ํ•  ์ฃผ์†Œ (์ธ์ฆ)
                .failureUrl("/atuhs/login-form?error")
                //์‹ค์ œ ๋กœ๊ทธ์ธ์ด ์‹คํŒจํ–ˆ์„ ๊ฒฝ์šฐ ์–ด๋–ค ํŽ˜์ด์ง€๋ฅผ ๋ณด๋‚ผ ๊ฑด์ง€. ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ• ๊ฑด์ง€ ์„ค
                .and()
                .authorizeHttpRequests() //ํด๋ผ์ด์–ธํŠธ์˜ ์š”์ฒญ์ด ๋“ค์–ด์˜ค๋ฉด ์ ‘๊ทผ ๊ถŒํ•œ์„ ํ™•์ธ
                .anyRequest()
                .permitAll();
                    //ํด๋ผ์ด์–ธํŠธ์˜ ๊ถŒํ•œ์„ ๋ณผ๊ฑด๋ฐ ์ „์ฒด ๋‹ค ๊ฐ€๋Šฅ
        return http.build();
    }
  1. (1) ์—์„œ CSRF(Cross-Site Request Forgery) ๊ณต๊ฒฉ์— ๋Œ€ํ•œ Spring Security์— ๋Œ€ํ•œ ์„ค์ •์„ ๋น„ํ™œ์„ฑํ™”
    • Spring Security๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ์•„๋ฌด ์„ค์ •์„ ํ•˜์ง€ ์•Š์œผ๋ฉด csrf() ๊ณต๊ฒฉ์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ํด๋ผ์ด์–ธํŠธ๋กœ๋ถ€ํ„ฐ CSRF Token์„ ์ˆ˜์‹  ํ›„, ๊ฒ€์ฆํ•œ๋‹ค.
    • but ์šฐ๋ฆฌ๋Š” ๋กœ์ปฌํ™˜๊ฒฝ์ด๋ผ์„œ ๊ณต๊ฒฉ์— ๋Œ€ํ•œ ์„ค์ •์ด ๋”ฐ๋กœ ํ•„์š”ํ•˜์ง€ ์•Š์Œ
  2. (2) ์—์„œ ๊ธฐ๋ณธ์ ์ธ ์ธ์ฆ ๋ฐฉ๋ฒ•์„ ํผ๋กœ๊ทธ์ธ ๋ฐฉ์‹์œผ๋กœ ์ง€์ •
  3. (3)์˜ .loginPage(โ€œ/auths/login-formโ€) ์—์„œ ํ•ด๋‹น ํŒŒ๋ผ๋ฏธํ„ฐ์˜ URL์€ AuthController์˜ loginForm() ํ•ธ๋“ค๋Ÿฌ ๋ฉ”์„œ๋“œ์— ์š”์ฒญ์„ ์ „์†กํ•˜๋Š” URL์ด๋‹ค.
  4. ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ loginProcessingUrl(โ€œ/process_loginโ€) ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ๋กœ๊ทธ์ธ ์ธ์ฆ ์š”์ฒญ์„ ์ˆ˜ํ–‰ํ•  ์š”์ฒญ URL์„ ์ง€์ •
    โ†’ โ€œ/process_loginโ€œ์€ ์šฐ๋ฆฌ๊ฐ€ ๋งŒ๋“ค์–ด ๋‘” login.html์—์„œ form ํƒœ๊ทธ์˜ action ์†์„ฑ์— ์ง€์ •ํ•œ URL๊ณผ ๋™์ผ
    • ์ปค์Šคํ…€ ๋กœ๊ทธ์ธ ํ™”๋ฉด์—์„œ ๋กœ๊ทธ์ธ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด
      formํƒœ๊ทธ์˜ action์†์„ฑ์— ์ง€์ •๋œ /process_login URL๋กœ ์‚ฌ์šฉ์ž ์ธ์ฆ์„ ์œ„ํ•œ ์ •๋ณด๋ฅผ ์ „์†ก(์ด๋ฉ”์ผ๊ณผ ํŒจ์Šค์›Œ๋“œ)

3๏ธโƒฃ ์ปค์Šคํ…€ ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€(login.html)์˜ ์ธ์ฆ์‹คํŒจ ๋ฉ”์„ธ์ง€ ์ถ”๊ฐ€

${param.error}์˜ ๊ฐ’์„ ํ†ตํ•ด ๋กœ๊ทธ์ธ ์ธ์ฆ ์‹คํŒจ ๋ฉ”์‹œ์ง€ ํ‘œ์‹œ ์—ฌ๋ถ€๋ฅผ ๊ฒฐ์ •

${param.error}๋Š” Spring Security Configuration์—์„œ

failureUrl(โ€œ/auths/login-form?errorโ€)์˜ ?error ๋ถ€๋ถ„์— ํ•ด๋‹นํ•˜๋Š” ์ฟผ๋ฆฌ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์˜๋ฏธ

1
2
3
<div class="row alert alert-danger center" role="alert" th:if="${param.error != null}">
	<div>๋กœ๊ทธ์ธ ์ธ์ฆ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค.</div>
</div>

4๏ธโƒฃ ์š”์ฒญ URI์— ๋Œ€ํ•œ ์ ‘๊ทผ ๊ถŒํ•œ ๋ถ€์—ฌ

SecurityConfiguration์—์„œ filterChain๋ฉ”์„œ๋“œ ์ˆ˜์ •

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Configuration
public class SecurityConfiguration {
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .formLogin()
            .loginPage("/auths/login-form")
            .loginProcessingUrl("/process_login")
            .failureUrl("/auths/login-form?error")
            .and()
            .exceptionHandling().accessDeniedPage("/auths/access-denied")
            .and()
            .authorizeHttpRequests(authorize -> authorize             
                    .antMatchers("/orders/**").hasRole("ADMIN") 
                    .antMatchers("/members/my-page").hasRole("USER")
                    .antMatchers("/**").permitAll()
            );
        return http.build();
    }

2๏ธโƒฃ์—์„œ ๊ตฌํ˜„๋œ filterChain()์—์„œ .authorizeHttpRequests().anyRequest().permitAll(); ์„ค์ •์„ ํ†ตํ•ด ๋กœ๊ทธ์ธ ์ธ์ฆ์— ์„ฑ๊ณตํ•  ๊ฒฝ์šฐ,
๋ชจ๋“  ํ™”๋ฉด์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋„๋ก ํ–ˆ๋˜ ๋ถ€๋ถ„์„ ์‚ฌ์šฉ์ž์˜ Role ๋ณ„๋กœ request URI์— ์ ‘๊ทผ ๊ถŒํ•œ์ด ๋ถ€์—ฌ๋˜๋„๋ก ์ˆ˜์ •

  1. exceptionHandling().accessDeniedPage(โ€œ/auths/access-deniedโ€)๋ฅผ ํ†ตํ•ด
    ๊ถŒํ•œ์ด ์—†๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ํŠน์ • request URI์— ์ ‘๊ทผํ•  ๊ฒฝ์šฐ ๋ฐœ์ƒํ•˜๋Š” 403(Forbidden) ์—๋Ÿฌ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ํŽ˜์ด์ง€๋ฅผ ์„ค์ •
    • exceptionHandling () : Exception ์ฒ˜๋ฆฌ๋ฅผ ์ˆ˜ํ–‰
    • ๋ฆฌํ„ดํ•˜๋Š” ExceptionHandlingConfigure ๊ฐ์ฒด๋ฅผ ํ†ตํ•ด ๊ตฌ์ฒด์ ์ธ Exception ์ฒ˜๋ฆฌ๊ฐ€๋Šฅ
    • accessDeniedPage() ๋ฉ”์„œ๋“œ๋Š” 403 ์—๋Ÿฌ ๋ฐœ์ƒ ์‹œ, ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ง€์ •ํ•œ URL๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ ๋˜๋„๋ก ํ•ด์ค€๋‹ค.
  2. authorizeHttpRequests() ๋ฉ”์„œ๋“œ์—์„œ ๋žŒ๋‹ค์‹์œผ๋กœ request URI์— ๋Œ€ํ•œ ์ ‘๊ทผ ๊ถŒํ•œ์„ ๋ถ€์—ฌ
    • antMatchers() ๋ฉ”์„œ๋“œ๋Š” ์ด๋ฆ„ ๊ทธ๋Œ€๋กœ ant๋ผ๋Š” ๋นŒ๋“œ ํˆด์—์„œ ์‚ฌ์šฉ๋˜๋Š” Path Pattern์„ ์ด์šฉํ•ด์„œ ๋งค์น˜๋˜๋Š” URL์„ ํ‘œํ˜„
    • **๋Š” /orders๋กœ ์‹œ์ž‘ํ•˜๋Š” ๋ชจ๋“  ํ•˜์œ„ URL์„ ํฌํ•จ
    • orders/*๋ผ๋Š” URL์„ ์ง€์ •ํ–ˆ๋‹ค๋ฉด /orders/1๊ณผ ๊ฐ™์ด /orders์˜ ํ•˜์œ„ URL์˜ depth๊ฐ€ 1์ธ URL๋งŒ ํฌํ•จ
    • antMatchers("/members/my-page").hasRole("USER")์€ USER Role์„ ๋ถ€์—ฌ๋ฐ›์€ ์‚ฌ์šฉ์ž๋งŒ
      /members/my-page URL์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค.
    • .antMatchers("/**").permitAll()์€ ์•ž์—์„œ ์ง€์ •ํ•œ URL ์ด์™ธ์˜ ๋‚˜๋จธ์ง€ ๋ชจ๋“  URL์€ Role์— ์ƒ๊ด€์—†์ด ์ ‘๊ทผ์ด ๊ฐ€๋Šฅํ•จ์„ ์˜๋ฏธ
  3. antMatchers()๋ฅผ ์ด์šฉํ•œ ์ ‘๊ทผ ๊ถŒํ•œ ๋ถ€์—ฌ ์‹œ, ์ฃผ์˜ ์‚ฌํ•ญ
    • ๋งŒ์•ฝ์— authorizeHttpRequests() ๋ฉ”์„œ๋“œ์—์„œ .antMatchers("/**").permitAll() ์ด ์ œ์ผ ์•ž์— ์œ„์น˜ํ•˜๊ฒŒ ๋˜๋ฉด
      Role์— ์ƒ๊ด€์—†์ด ๋ชจ๋“  requestURL์— ์ ‘๊ทผ ํ—ˆ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋‹ค์Œ์— ์˜ค๋Š” ์ ‘๊ทผ ๊ถŒํ•œ ์กฐ๊ฑด์€ ์ œ ๊ธฐ๋Šฅ์„ ํ•˜์ง€ ๋ชปํ•˜๊ฒŒ ๋œ๋‹ค.
    • ํ•ญ์ƒ โญ ๋” ๊ตฌ์ฒด์ ์ธ URL ๊ฒฝ๋กœ๋ถ€ํ„ฐ ์ ‘๊ทผ ๊ถŒํ•œ์„ ๋ถ€์—ฌํ•œ ๋‹ค์Œ ๋œ ๊ตฌ์ฒด์ ์ธ URL ๊ฒฝ๋กœ์— ์ ‘๊ทผ ๊ถŒํ•œ์„ ๋ถ€์—ฌํ•˜๋Š” ์Šต๊ด€ ๋“ค์ด๊ธฐ.

    ์ฐธ๊ณ ๋กœ Ant๋Š” Maven๊ณผ Gradle์— ๋ฐ€๋ ค์„œ ๊ฑฐ์˜ ์‚ฌ์šฉ๋˜์ง€ ์•Š๋Š” ๋นŒ๋“œ ํˆด,
    but, Ant์—์„œ ์‚ฌ์šฉ๋˜๋Š” Ant Pattern์€ URL ๊ฒฝ๋กœ ๋“ฑ์„ ์ง€์ •ํ•˜๊ธฐ ์œ„ํ•œ Pattern ํ‘œํ˜„์‹์œผ๋กœ ์—ฌ๋Ÿฌ ์˜คํ”ˆ ์†Œ์Šค์—์„œ ์‚ฌ์šฉ๋˜๊ณ  ์žˆ๋‹ค.
    Maven๋„ xml์„ ์‚ฌ์šฉํ•ด์•ผํ•ด์„œ gradle๋กœ ๋งŽ์ด ๋„˜์–ด๊ฐ€๋Š” ์ถ”์„ธ

5๏ธโƒฃ ๊ด€๋ฆฌ์ž ๊ถŒํ•œ์„ ๊ฐ€์ง„ ์‚ฌ์šฉ์ž ์ถ”๊ฐ€

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
...
@Configuration
public class SecurityConfiguration {
    ...
    ...
    @Bean
    public InMemoryUserDetailsManager userDetailsService() {
        UserDetails user =
                User.withDefaultPasswordEncoder()
                        .username("kevin@gmail.com")
                        .password("1111")
                        .roles("USER")
                        .build();
                        
        UserDetails admin =
                User.withDefaultPasswordEncoder()
                        .username("admin@gmail.com")
                        .password("2222")
                        .roles("ADMIN")
                        .build();

        return new InMemoryUserDetailsManager(user, admin);
    }
}

6๏ธโƒฃ ๋กœ๊ทธ์ธ ํ•œ ์‚ฌ์šฉ์ž ์•„์ด๋”” ํ‘œ์‹œ ๋ฐ ์‚ฌ์šฉ์ž ๋กœ๊ทธ์•„์›ƒ.

๋กœ๊ทธ์ธ ํ•œ ์‚ฌ์šฉ์ž ํ‘œ์‹œ & ๋กœ๊ทธ์ธ ํ•œ ์‚ฌ์šฉ์ž ๋กœ๊ทธ์•„์›ƒ ํ•˜๊ธฐ & ๋งˆ์ดํŽ˜์ด์ง€ ๋งํฌ๋Š” ๋กœ๊ทธ์ธ ํ•œ ์‚ฌ์šฉ์ž์—๊ฒŒ๋งŒ ๋ณด์ด๊ฒŒ ํ•˜๊ธฐ

  1. header.html ์ˆ˜์ • โ†’ ๋กœ๊ทธ์•„์›ƒ ๋ฐ ๊ถŒํ•œ ๋ณ„ ๋ฉ”๋‰ดํ‘œ์‹œํ•˜๊ธฐ

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    
     <html xmlns:th="<http://www.thymeleaf.org>"
           xmlns:sec="<http://www.thymeleaf.org/thymeleaf-extras-springsecurity5>"> 
           <!-- (1) -->
         <body>
             <div align="right" th:fragment="header">
                 <a href="/members/register" class="text-decoration-none">ํšŒ์›๊ฐ€์ž…</a> |
                 <span sec:authorize="isAuthenticated()"> <!-- (2) -->
                     <span sec:authorize="hasRole('USER')">  <!-- (3) -->
                         <a href="/members/my-page" class="text-decoration-none">๋งˆ์ดํŽ˜์ด์ง€</a> |
                     </span>
                     <a href="/logout" class="text-decoration-none">๋กœ๊ทธ์•„์›ƒ</a>  <!-- (4) -->
                     <span th:text="${#authentication.name}">ํ™๊ธธ๋™</span>๋‹˜  <!-- (5) -->
                 </span>
        
                 <span sec:authorize="!isAuthenticated()"> <!-- (6) -->
                     <a href="/auths/login-form" class="text-decoration-none">๋กœ๊ทธ์ธ</a>
                 </span>
             </div>
         </body>
     </html>
        
    
    • ํƒ€์ž„๋ฆฌํ”„ ๊ธฐ๋ฐ˜์˜ HTML ํ…œํ”Œ๋ฆฟ์—์„œ ์‚ฌ์šฉ์ž์˜ ์ธ์ฆ ์ •๋ณด๋‚˜ ๊ถŒํ•œ ์ •๋ณด๋ฅผ ์ด์šฉํ•ด ์–ด๋–ค ๋กœ์ง์„ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š”
      ๋จผ์ € (1)๊ณผ ๊ฐ™์ด sec ํƒœ๊ทธ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•œ XML ๋„ค์ž„์ŠคํŽ˜์ด์Šค๋ฅผ ์ง€์ •, ์ง€์ •ํ•˜์ง€ ์•Š์•„๋„ ๋™์ž‘ํ•˜์ง€๋งŒ ํƒœ๊ทธ์— ์—๋Ÿฌ ํ‘œ์‹œ ๋‚จ.
    • (2) : ํƒœ๊ทธ ๋‚ด๋ถ€์—์„œ sec:authorize="isAuthenticated()"๋ฅผ ์ง€์ •ํ•˜๋ฉด ํ˜„์žฌ ํŽ˜์ด์ง€์— ์ ‘๊ทผํ•œ ์‚ฌ์šฉ์ž๊ฐ€ ์ธ์ฆ์— ์„ฑ๊ณตํ•œ ์‚ฌ์šฉ์ž์ธ์ง€๋ฅผ ์ฒดํฌ
      • โ‡’ isAuthenticated()์˜ ๊ฐ’์ด true์ด๋ฉด ํƒœ๊ทธ ํ•˜์œ„์— ํฌํ•จ๋œ ์ฝ˜ํ…์ธ ๋ฅผ ํ™”๋ฉด์— ํ‘œ์‹œ
    • (3) : ์ธ์ฆ์— ์„ฑ๊ณตํ•œ ์‚ฌ์šฉ์ž๋ผ๋ฉด ๋งˆ์ดํŽ˜์ด์ง€๋Š” USER Role์„ ๊ฐ€์ง„ ์‚ฌ์šฉ์ž์—๊ฒŒ๋งŒ ํ‘œ์‹œ ๋˜๋„๋ก ํ•œ๋‹ค.
    • (4) : (2)์—์„œ ๋กœ๊ทธ์ธ์— ์„ฑ๊ณตํ•œ ์‚ฌ์šฉ์ž๋Š” [๋กœ๊ทธ์ธ] ๋ฉ”๋‰ด ๋Œ€์‹  [๋กœ๊ทธ์•„์›ƒ]์ด ํ‘œ์‹œ๋œ๋‹ค.
      • href="/logout"์—์„œ โ€œ/logoutโ€ URL์€ SecutiryConfiguration ํด๋ž˜์Šค์—์„œ ์„ค์ •ํ•œ ๊ฐ’๊ณผ ๊ฐ™์•„์•ผ ํ•œ๋‹ค.
    • (5) : th:text="${#authentication.name}"๋ฅผ ํ†ตํ•ด ๋กœ๊ทธ์ธ ์‚ฌ์šฉ์ž์˜ username์„ ํ‘œ์‹œ โ†’ ๋กœ๊ทธ์ธ ํ•œ ์‚ฌ์šฉ์ž๋Š” username์ด ํ‘œ์‹œ๋˜๊ณ 
    • (6) : sec:authorize=โ€!isAuthenticated()โ€๋ฅผ ํ†ตํ•ด ๋กœ๊ทธ์ธ ํ•œ ์‚ฌ์šฉ์ž๊ฐ€ ์•„๋‹ˆ๋ผ๋ฉด [๋กœ๊ทธ์ธ] ๋ฒ„ํŠผ์ด ํ‘œ์‹œ๋˜๋„๋ก ํ•œ๋‹ค.
      • sec ํƒœ๊ทธ ์‚ฌ์šฉ์‹œ build.gradle์—์„œ ์˜์กด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ถ”๊ฐ€ ํ•ด์•ผํ•จ
      • implementation โ€˜org.thymeleaf.extras:thymeleaf-extras-springsecurity5โ€™


  2. SecurityConfiguration ํด๋ž˜์Šค์— ๋กœ๊ทธ์•„์›ƒ ์„ค์ • ์ถ”๊ฐ€

    • ๋กœ๊ทธ์•„์›ƒ์„ ๋ˆŒ๋ €์„ ๋•Œ๋Š” ์Šคํ”„๋ง ์€ ๋กœ๊ทธ์•„์›ƒ url์„ ์„ค์ • ์‹œ ๊ธฐ๋ณธ์ ์œผ๋กœ ๊ถŒํ•œ์ด ์‚ฌ๋ผ์ง€๊ฒŒ ์„ค์ • ๋˜์–ด ์žˆ์Œ. โ†’ ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€๋กœ ์ด๋™ํ•˜๊ฒŒ ๋˜์–ด์žˆ์Œ,
    • ์šฐ๋ฆฌ๊ฐ€ ๋กœ๊ทธ์•„์›ƒ์„ ์„ค์ •ํ•˜๋ฉด ์„ฑ๊ณตํ–ˆ์„ ๋•Œ ๋ฃจํŠธ๋กœ ์ด๋™ํ•˜๊ฒŒ ์ฝ”๋“œ ์ถ”๊ฐ€.

    • logout() ๋ฉ”์„œ๋“œ๋Š” ๋กœ๊ทธ์•„์›ƒ ์„ค์ •์„ ์œ„ํ•œ LogoutConfigurer๋ฅผ ๋ฆฌํ„ด
    • logoutUrl(โ€œ/logoutโ€)์„ ํ†ตํ•ด ์‚ฌ์šฉ์ž๊ฐ€ ๋กœ๊ทธ์•„์›ƒ์„ ์ˆ˜ํ–‰ํ•˜๊ธฐ ์œ„ํ•œ request URL์„ ์ง€์ •
      โ‡’ โญ ์—ฌ๊ธฐ์„œ ์„ค์ •ํ•œ URL์€ header.html์˜ ๋กœ๊ทธ์•„์›ƒ ๋ฉ”๋‰ด์— ์ง€์ •ํ•œ href="/logout"๊ณผ ๋™์ผํ•ด์•ผ ํ•œ๋‹ค.
    • .logoutSuccessUrl(โ€œ/โ€) : ๋กœ๊ทธ์•„์›ƒ์„ ์„ฑ๊ณต์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•œ ์ดํ›„ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ ํ•  URL์„ ์ง€์ •, ์—ฌ๊ธฐ์„œ๋Š” ๋ฉ”์ธํ™”๋ฉด์œผ๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ ํ•˜๋„๋ก ์ง€์ •.
    • ๋กœ๊ทธ์•„์›ƒ ํด๋ฆญ ์‹œ ํ—ค๋”๊ฐ€ ๋ฐ”๋€Œ๊ฒŒ ๋จ.

ํšŒ์›๊ฐ€์ž… ๊ธฐ๋Šฅ ๊ตฌํ˜„

์—ฌ๊ธฐ๊นŒ์ง€ Security filterChain์œผ๋กœ ์ธ์ฆ์ธ๊ฐ€๋ฅผ ๊ตฌํ˜„ โ†’ ์ด์ œ ์ธ๋ฉ”๋ชจ๋ฆฌDB๋กœ ๋ณ€๊ฒฝ

์ด UserDetails๊ฐ์ฒด๊ฐ€ ์ƒˆ๋กœ ์ƒ์„ฑ๋˜์–ด์•ผํ•จ

์•„์งDB๋Š” ์‚ฌ์šฉํ•˜์ง€ ์•Š์„ ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์— InMemoryUserDetailsManager๋กœ ๋ฐ˜ํ™˜ํ•  ์˜ˆ์ •

[ํšŒ์› ๊ฐ€์ž… ํผ์„ ํ†ตํ•œ InMemory User ๋“ฑ๋ก]

ํšŒ์› ๊ฐ€์ž… ํผ์„ ํ†ตํ•ด InMemory User๋ฅผ ๋“ฑ๋กํ•˜๊ธฐ ์œ„ํ•œ ์ž‘์—… ์ˆœ์„œ

  • PasswordEncoder Bean ๋“ฑ๋ก
  • MemberService Bean ๋“ฑ๋ก์„ ์œ„ํ•œ JavaConfiguration ๊ตฌ์„ฑ
  • InMemoryMemberService ํด๋ž˜์Šค ๊ตฌํ˜„

1๏ธโƒฃ PasswordEncoder Bean ๋“ฑ๋ก

PasswordEncoder๋Š” Spring Security์—์„œ ์ œ๊ณตํ•˜๋Š” ํŒจ์Šค์›Œ๋“œ ์•”ํ˜ธํ™” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ์ด๋‹ค.

PasswordEncoder๋Š” ๋‹ค์–‘ํ•œ ์•”ํ˜ธํ™” ๋ฐฉ์‹์„ ์ œ๊ณตํ•˜๋ฉฐ,
Spring Security์—์„œ ์ง€์›ํ•˜๋Š” PasswordEncoder์˜ ๋””ํดํŠธ ์•”ํ˜ธํ™” ์•Œ๊ณ ๋ฆฌ์ฆ˜์€ bcrypt์ด๋‹ค.

  • ํšŒ์› ๊ฐ€์ž… ํผ์„ ํ†ตํ•ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ์ „๋‹ฌ๋˜๋Š” ํŒจ์Šค์›Œ๋“œ๋Š” ์•”ํ˜ธํ™”๋˜์ง€ ์•Š์€ ํ”Œ๋ ˆ์ธ ํ…์ŠคํŠธ(Plain Text)
    โ†’ ์•”ํ˜ธํ™”ํ•˜๊ธฐ์œ„ํ•ด passwordEncorder์‚ฌ์šฉ
  • ํšŒ์› ๊ฐ€์ž… ํผ์—์„œ ์ „๋‹ฌ๋ฐ›์€ ํŒจ์Šค์›Œ๋“œ๋Š” InMemory User๋กœ ๋“ฑ๋กํ•˜๊ธฐ ์ „์— ์•”ํ˜ธํ™”๋˜์–ด์•ผ ํ•œ๋‹ค.

SecurityConfiguration ํด๋ž˜์Šค์—์„œ PasswordEncoder๋ฅผ Bean์œผ๋กœ ๋“ฑ๋ก

1
2
3
4
5
6
7
8
9
@Configuration
public class SecurityConfiguration {
...

@Bean
public PasswordEncoder passwordEncoder(){
    return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }
}

PasswordEncoderFactories.createDelegatingPasswordEncoder();๋ฅผ ํ†ตํ•ด DelegatingPasswordEncoder๋ฅผ ๋จผ์ € ์ƒ์„ฑํ•˜๋Š”๋ฐ,
์ด DelegatingPasswordEncoder๊ฐ€ ์‹ค์งˆ์ ์œผ๋กœ PasswordEncoder ๊ตฌํ˜„ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•ด ์ค€๋‹ค.

์ฐธ๊ณ ) ๊ฐœ๋ฐœํ™˜๊ฒฝ์—์„œ๋งŒ ์ธ๋ฉ”๋ชจ๋ฆฌ DB ์‚ฌ์šฉํ•  ๊ฑฐ๊ณ  @configuration์œผ๋กœ ๋นˆ๊ฐ์ฒด๋ฅผ ๋งŒ๋“œ๋Š” ๊ฒƒ์ž„. @Configuration+@bean์„ ์‚ฌ์šฉํ•˜์ง€์•Š๊ณ  @Component ์‚ฌ์šฉ ํ•ด๋„ ๋จ.

2๏ธโƒฃ MemberService Bean ๋“ฑ๋ก์„ ์œ„ํ•œ JavaConfiguration ๊ตฌ์„ฑ

  1. MemberSercice

    ํšŒ์› ๊ฐ€์ž… ํผ์—์„œ ์ „๋‹ฌ๋ฐ›์€ ์ •๋ณด๋ฅผ ์ด์šฉํ•ด ์ƒˆ๋กœ์šด ์‚ฌ์šฉ์ž๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๊ธฐ๋Šฅ๋งŒ ์žˆ์œผ๋ฉด ๋˜๋ฏ€๋กœ createMember() ํ•˜๋‚˜๋งŒ ๊ตฌํ˜„ํ•˜๋Š” ๊ตฌํ˜„์ฒด๊ฐ€ ์žˆ์œผ๋ฉด ๋œ๋‹ค.

    1
    2
    3
    4
    5
    6
    
     package com.springboot.member;
        
     public interface MemberService {
         Member createMember(Member member);
     }
        
    
  2. InMemoryMemberService โ†’ MemberService ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๊ตฌํ˜„ ํด๋ž˜์Šค

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
     package com.springboot.member;
        
     public class InMemoryMemberService implements MemberService {
         public Member createMember(Member member) {
        
             return null; // ๋’ค์—์„œ ๊ตฌํ˜„ ํ•  ์˜ˆ์ •์ด๋‹ค.
         }
     }
        
    
  3. DBMemberService ํด๋ž˜์Šค โ†’ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— User๋ฅผ ๋“ฑ๋กํ•˜๊ธฐ ์œ„ํ•œ MemberService ์ธํ„ฐํŽ˜์ด์Šค์˜ ๊ตฌํ˜„ ํด๋ž˜์Šค

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    
     package com.springboot.member;
        
     import org.springframework.transaction.annotation.Transactional;
        
     @Transactional
     public class DBMemberService implements MemberService {
         public Member createMember(Member member) {
              return null; // InMemory User ๋“ฑ๋ก์ด ๋๋‚œ ๋‹ค์Œ ํ•™์Šต๋‚ด์šฉ,  ์ด์–ด์„œ ๋ฐ”๋กœ ๊ตฌํ˜„
         }
     }
        
    
  4. JavaConfiguration ๊ตฌ์„ฑ : MemberService Bean ๋“ฑ๋ก์„ ์œ„ํ•œ JavaConfiguration ๊ตฌ์„ฑ

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    
     package com.springboot.config;
        
     import com.springboot.member.InMemoryMemberService;
     import com.springboot.member.MemberService;
     import org.springframework.context.annotation.Bean;
     import org.springframework.context.annotation.Configuration;
     import org.springframework.security.crypto.password.PasswordEncoder;
     import org.springframework.security.provisioning.UserDetailsManager;
        
     @Configuration
     public class JavaConfiguration {
            
         @Bean
         public MemberService inMemoryMemberService(UserDetailsManager userDetailsManager,
                                                    PasswordEncoder passwordEncoder) {
             return new InMemoryMemberService(userDetailsManager, passwordEncoder);
         }
     }
        
    
    • MemberService ์ธํ„ฐํŽ˜์ด์Šค์˜ ๊ตฌํ˜„ ํด๋ž˜์Šค์ธ InMemoryMemberService๋ฅผ Spring Bean์œผ๋กœ ๋“ฑ๋ก
    • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๋™ ์—†์ด ๋ฉ”๋ชจ๋ฆฌ์— Spring Security์˜ User๋ฅผ ๋“ฑ๋กํ•ด์•ผ ํ•˜๋ฏ€๋กœ UserDetailsManager ๊ฐ์ฒด๊ฐ€ ํ•„์š”
    • User ๋“ฑ๋ก ์‹œ, ํŒจ์Šค์›Œ๋“œ๋ฅผ ์•”ํ˜ธํ™”ํ•œ ํ›„์— ๋“ฑ๋กํ•ด์•ผ ํ•˜๋ฏ€๋กœ Spring Security์—์„œ ์ œ๊ณตํ•˜๋Š” PasswordEncoder ๊ฐ์ฒด๊ฐ€ ํ•„์š”
    • ๋‘ ๊ฐ์ฒด๋ฅผ InMemoryMemberService ๊ฐ์ฒด ์ƒ์„ฑ ์‹œ, DI ํ•ด ์ฃผ์–ด์•ผ ํ•œ๋‹ค.

3๏ธโƒฃ InMemoryMemberService ๊ตฌํ˜„

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
package com.springboot.member;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.UserDetailsManager;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

//(1) @Service ์• ๋„ˆํ…Œ์ด์…˜์€ ์—ฌ๊ธฐ์— ๋‹ฌ์ง€ ์•Š๋Š”๋‹ค, ์ค‘๋ณต์•ˆ๋จ. ๋ฐ”๋กœ ์œ„ JavaConfiguration์—์„œ ๋ฐ˜ํ™˜๋˜๋Š” 
// InMemoryMemberService๊ฐ€ ๋นˆ์œผ๋กœ ๋“ฑ๋ก๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ.
public class InMemoryMemberService implements MemberService {
     //(2)
    private final UserDetailsManager userDetailsManager;
  
    private final PasswordEncoder passwordEncoder;

    public InMemoryMemberService(UserDetailsManager userDetailsManager, PasswordEncoder passwordEncoder) {
        this.userDetailsManager = userDetailsManager;
        this.passwordEncoder = passwordEncoder;
    }

    @Override
    public Member createMember(Member member) {
		  //(3)
        List<GrantedAuthority> authorities = createAuthories(Member.MemberRole.ROLE_USER.name());
        //์ผ๋ฐ˜์ ์ธ ํšŒ์›๊ฐ€์ž…์€ user๋กœ ๊ถŒํ•œ ์„ค์ •ํ•ด์คŒ
        //ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋“ค์–ด์˜จ member๋Š” dto์—์„œ ๋ณ€ํ™˜ ๋œ ๊ฒƒ , ๋น„๋ฐ€๋ฒˆํ˜ธ๋Š” ์•„์ง ํ‰๋ฌธ์ด๋‹ค. ์•”ํ˜ธํ™”ํ•ด์•ผ ํ•จ.
        //์ธ๊ฐ€๋Š” List๋กœ ๊ด€๋ฆฌ ํ•œ๋‹ค..

        String encryptedPassword = passwordEncoder.encode(member.getPassword());
       
       //(4)
        UserDetails userDetails = new User(member.getEmail(), encryptedPassword, authorities);
                                    //์šฐ๋ฆฌ๊ฐ€ ๋งŒ๋“  ์œ ์ € ์•„๋‹˜.

        userDetailsManager.createUser(userDetails); 
		        //UserDetailsManager์˜ createUser() ๋ฉ”์„œ๋“œ๋ฅผ ์ด์šฉํ•ด์„œ User๋ฅผ ๋“ฑ๋ก
        return member;
    }

		//(5) 
    private List<GrantedAuthority> createAuthories(String ...roles){
        return Arrays.stream(roles)
                .map(role-> new SimpleGrantedAuthority(role))
                .collect(Collectors.toList());

        //๊ถŒํ•œ์„ ๋ฆฌ์Šค๋ฅดํ†  ๋ฐ›๋Š” ์ด์œ 
        //ํ•œ ์‚ฌ์šฉ์ž๊ฐ€ ์—ฌ๋Ÿฌ ๊ฐœ์˜ ๊ถŒํ•œ์„ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ
        //์˜ˆ๋ฅผ ๋“ค์–ด, ํ•œ ์‚ฌ์šฉ์ž๊ฐ€ ROLE_USER์™€ ROLE_ADMIN ๊ถŒํ•œ์„ ๋™์‹œ์— ๊ฐ€์งˆ ์ˆ˜ ์žˆ๋‹ค.
        // ๋”ฐ๋ผ์„œ ๊ถŒํ•œ์„ ๋ฆฌ์ŠคํŠธ๋กœ ๊ด€๋ฆฌํ•˜์—ฌ ์œ ์—ฐํ•˜๊ฒŒ ์—ฌ๋Ÿฌ ๊ถŒํ•œ์„ ๋ถ€์—ฌํ•  ์ˆ˜ ์žˆ๋‹ค.

    }
}

  • (1) ์—์„œ @Service ์• ๋„ˆํ…Œ์ด์…˜์€ ๋‹ฌ์ง€ ์•Š๋Š”๋‹ค โ†’ ๋นˆ์€ ์ค‘๋ณต์ด ๋˜๋ฉด ์•ˆ๋œ๋‹ค.
    ๋ฐ”๋กœ ์œ„ JavaConfiguration์—์„œ ๋ฐ˜ํ™˜๋˜๋Š”InMemoryMemberService๊ฐ€ ๋นˆ์œผ๋กœ ๋“ฑ๋ก๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ.
  • (2) ์—์„œ UserDetailsManager์™€ PasswordEncoder๋ฅผ DI ๋ฐ›๋Š”๋‹ค.
    • UserDetailsManager๋Š” Spring Security์˜ User๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ๊ด€๋ฆฌ์ž ์—ญํ• ์„ ํ•œ๋‹ค..
    • SecurityConfiguration์—์„œ ๋นˆ ์„ค์ •์„ ํ•œ ์ด์œ ๋Š” ์—ฌ๊ธฐ์„œ ์ฃผ์ž…ํ•˜๋ ค๊ณ 
      โ‡’ PSA (Policy Service Abstraction) ์›์น™์— ๋”ฐ๋ผ ์ถ”์ƒ ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ฉ”์„œ๋“œ ์ด๋ฆ„์„ ๋ฐ”๊พธ์ง€ ์•Š๊ณ ๋„ ์ผ๊ด€๋œ ๋ฐฉ์‹์œผ๋กœ ์ฃผ์ž… ๊ฐ€๋Šฅ
    • UserDetailsManager ์ธํ„ฐํŽ˜์ด์Šค์˜ ํ•˜์œ„ ํƒ€์ž…์€InMemoryUserDetailsManager๋ผ๋Š” ์‚ฌ์‹ค ๊ธฐ์–ตํ•˜๊ธฐ.
  • (3) : Spring Security์—์„œ User๋ฅผ ๋“ฑ๋กํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ํ•ด๋‹น User์˜ ๊ถŒํ•œ(Authority)์„ ์ง€์ •ํ•ด ์ฃผ์–ด์•ผ ํ•œ๋‹ค.
    • createAuthorities(Member.MemberRole.ROLE_USER.name());๋ฅผ ์ด์šฉํ•ด User์˜ ๊ถŒํ•œ ๋ชฉ๋ก์„ List<GrantedAuthority>๋กœ ์ƒ์„ฑ
    • Member ํด๋ž˜์Šค์—๋Š” MemberRole์ด๋ผ๋Š” enum์ด ์ •์˜๋˜์–ด์žˆ๋‹ค.

    • โญ Spring Security์—์„œ๋Š” SimpleGrantedAuthority๋ฅผ ์‚ฌ์šฉํ•ด Role ๋ฒ ์ด์Šค ํ˜•ํƒœ์˜ ๊ถŒํ•œ์„ ์ง€์ •ํ•  ๋•Œ
      โ€˜ROLE_โ€™ + ๊ถŒํ•œ ๋ช… ํ˜•ํƒœ๋กœ ์ง€์ •ํ•ด ์ฃผ์–ด์•ผ ํ•œ๋‹ค. ๊ทธ๋ ‡์ง€ ์•Š์„ ๊ฒฝ์šฐ ์ ์ ˆํ•œ ๊ถŒํ•œ ๋งคํ•‘์ด ์ด๋ฃจ์–ด์ง€์ง€ ์•Š๋Š”๋‹ค๋Š” ์‚ฌ์‹ค ๊ธฐ์–ตํ•˜๊ธฐ
  • (4)์—์„œ๋Š” Spring Security User๋กœ ๋“ฑ๋กํ•˜๊ธฐ ์œ„ํ•ด UserDetails๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.

    โญ Spring Security์—์„œ๋Š” Spring Security์—์„œ ๊ด€๋ฆฌํ•˜๋Š” User ์ •๋ณด๋ฅผ UserDetails๋กœ ๊ด€๋ฆฌํ•œ๋‹ค๋Š” ๊ฒƒ ๊ผญ ๊ธฐ์–ตํ•˜๊ธฐ

  • (5) ์˜ createAuthories ๋ฉ”์„œ๋“œ : Stream API๋ฅผ ์ด์šฉํ•ด ์ƒ์„ฑ์ž ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ํ•ด๋‹น User์˜ Role์„ ์ „๋‹ฌํ•˜๋ฉด์„œ SimpleGrantedAuthority ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•œ ํ›„, List<SimpleGrantedAuthority> ํ˜•ํƒœ๋กœ ๋ฆฌํ„ด

๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค ์—ฐ๋™์„ ํ†ตํ•œ ๋กœ๊ทธ์ธ ์ธ์ฆ

ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์ด๋ฏ€๋กœ DB๋„ ์ธ๋ฉ”๋ชจ๋ฆฌ๋กœ ์‚ฌ์šฉํ•  ์˜ˆ์ •

Custom UserDetailsService๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•

Spring Security์—์„œ๋Š” User์˜ ์ธ์ฆ ์ •๋ณด๋ฅผ ํ…Œ์ด๋ธ”์— ์ €์žฅํ•˜๊ณ , ํ…Œ์ด๋ธ”์— ์ €์žฅ๋œ ์ธ์ฆ ์ •๋ณด๋ฅผ ์ด์šฉํ•ด ์ธ์ฆ ํ”„๋กœ์„ธ์Šค๋ฅผ ์ง„ํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ๋ช‡ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์ด ์žˆ๊ณ  ๊ทธ ์ค‘ ํ•˜๋‚˜๊ฐ€ Custom UserDetailsService๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ.

1๏ธโƒฃ SecurityConfiguration์˜ ์„ค์ • ๋ณ€๊ฒฝ ๋ฐ ์ถ”๊ฐ€

  1. H2๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ ‘์†์„ ํ•˜๊ธฐ ์œ„ํ•ด ๋ทฐ๋‹จ์—์„œ SameOrigin์„ค์ • ํ•ด์•ผ ๋ณผ ์ˆ˜ ์žˆ์Œ.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    
     @Configuration
     public class SecurityConfiguration {
      @Bean
      public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        
             http. headers().frameOptions().sameOrigin()
                     //๊ฐ™์€ ํŽ˜์ด์ง€์ผ ๊ฒฝ์šฐ ํŽ˜์ด์ง€๋ Œ๋”๋ง์„ ํ—ˆ์šฉํ•˜๊ฒ ๋‹ค 
                     // ์‹ค๋ฌด์—์„œ๋„ ์•„๋งˆ ์•ˆ์“ธ ํ™•๋ฅ ์ด ๋†’์Œ.
                 .and()
                 .csrf().disable()
                 .formLogin()
                 ...
                 ...
                 return http.build();
                    
    
  2. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅ๋˜์–ด ์žˆ๋Š” ์ธ์ฆ ์ •๋ณด๋ฅผ ์‚ฌ์šฉํ•  ๊ฒƒ์ด๋ผ์„œ InMemory User๋ฅผ ์œ„ํ•œ ์„ค์ •๋“ค์€ ๋” ์ด์ƒ ํ•„์š” ์—†์œผ๋ฏ€๋กœ ์ œ๊ฑฐ,
    ์•„๋ž˜ ์ฝ”๋“œ๋Š” ๋‹ค ์ œ๊ฑฐํ•˜๋ฉด ๋œ๋‹ค.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    
        @Bean //์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ์˜ ๋„์›€์„ ๋ฐ›๊ธฐ ์œ„ํ•จ => ์‚ฌ์‹ค์€ javaconfiguration์—์„œ ์ฃผ์ž…ํ•˜๊ธฐ ์œ„ํ•จ
        public UserDetailsManager userDetailService(){
    //        UserDetails user =
    //                User.withDefaultPasswordEncoder()
    //                        .username("jerry@gmail.com") 
    //                        .password("wpfl")
    //                        .roles("USER") 
    //                        .build(); 
            UserDetails admin =
                    User.withDefaultPasswordEncoder()
                            .username("admin@gmail.com") //์ผ๋ฐ˜์ ์œผ๋กœ ํ†ต์šฉ๋˜๋Š” ์•„์ด๋””์˜ ๊ฐœ๋….
                            .password("ehowl")
                            .roles("ADMIN") //์ธ๊ฐ€ : ๊ถŒํ•œ์„ ๋ญ˜ ์ค„ ๊ฒƒ์ธ์ง€?
                            .build();
            return new InMemoryUserDetailsManager(admin);
           
        
        }
    

2๏ธโƒฃ JavaConfiguration์˜ Bean ๋“ฑ๋ก ๋ณ€๊ฒฝ

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— User์˜ ์ •๋ณด๋ฅผ ์ €์žฅํ•˜๊ธฐ ์œ„ํ•ด MemberService ์ธํ„ฐํŽ˜์ด์Šค์˜ ๊ตฌํ˜„ ํด๋ž˜์Šค๋ฅผ inMemoryMemberService์—์„œ DBMemberService๋กœ ๋ณ€๊ฒฝ

1
2
3
4
5
6
7
8
9
10
...
@Configuration
public class JavaConfiguration {
    @Bean
    public MemberService inMemoryMemberService(UserDetailsManager userDetailsManager,
                                               PasswordEncoder passwordEncoder) {
        return new InMemoryMemberService(userDetailsManager, passwordEncoder);
    }
}

โฌ‡๏ธโฌ‡๏ธ โฌ‡๏ธโฌ‡๏ธ

1
2
3
4
5
6
7
8
9
@Configuration
public class JavaConfiguration {
    // (1)
    @Bean
    public MemberService dbMemberService(MemberRepository memberRepository,
                                         PasswordEncoder passwordEncoder) {
        return new DBMemberService(memberRepository, passwordEncoder); (1-1)
    }
}

DBMemberService๋Š” ๋‚ด๋ถ€์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅํ•˜๊ณ ,
ํŒจ์Šค์›Œ๋“œ๋ฅผ ์•”ํ˜ธํ™”ํ•ด์•ผ ํ•˜๋ฏ€๋กœ MemberRepository์™€ PasswordEncoder ๊ฐ์ฒด๋ฅผ DI ํ•ด์ค€๋‹ค.

3๏ธโƒฃ DBMemberService ๊ตฌํ˜„

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Transactional
public class DBMemberService implements MemberService {
    private final MemberRepository memberRepository;
    private final PasswordEncoder passwordEncoder;

    public DBMemberService(MemberRepository memberRepository,
                             PasswordEncoder passwordEncoder) {
        this.memberRepository = memberRepository;
        this.passwordEncoder = passwordEncoder;
    }

    public Member createMember(Member member) {
        verifyExistsEmail(member.getEmail());
        String encryptedPassword = passwordEncoder.encode(member.getPassword());  // (1)
        member.setPassword(encryptedPassword);    // (2)

        Member savedMember = memberRepository.save(member);

        System.out.println("# Create Member in DB");
        return savedMember;
    }

    ...
    ...
}

  • (1)์—์„œ PasswordEncoder๋ฅผ ์ด์šฉํ•ด ํŒจ์Šค์›Œ๋“œ๋ฅผ ์•”ํ˜ธํ™”ํ•œ๋‹ค.
  • (2)์—์„œ ์•”ํ˜ธํ™”๋œ ํŒจ์Šค์›Œ๋“œ๋ฅผ password ํ•„๋“œ์— ๋‹ค์‹œ ํ• ๋‹นํ•œ๋‹ค.

4๏ธโƒฃ Custom UserDetailsService ๊ตฌํ˜„ - V1

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์กฐํšŒํ•œ User์˜ ์ธ์ฆ ์ •๋ณด๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์ธ์ฆ์„ ์ฒ˜๋ฆฌ

โญ UserDetailsService

Spring Security์—์„œ ์ œ๊ณตํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ ์ค‘ ํ•˜๋‚˜์ธ UserDetailsService๋Š” User ์ •๋ณด๋ฅผ ๋กœ๋“œ(load)ํ•˜๋Š” ํ•ต์‹ฌ ์ธํ„ฐํŽ˜์ด์Šค

์—ฌ๊ธฐ์„œ ๋กœ๋“œ(load)์˜ ์˜๋ฏธ๋Š” ์ธ์ฆ์— ํ•„์š”ํ•œ User ์ •๋ณด๋ฅผ ์–ด๋”˜๊ฐ€์—์„œ ๊ฐ€์ง€๊ณ  ์˜จ๋‹ค๋Š” ์˜๋ฏธ์ด๋ฉฐ, ์—ฌ๊ธฐ์„œ ๋งํ•˜๋Š” โ€˜์–ด๋”˜๊ฐ€โ€™๋Š” ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ๋  ์ˆ˜๋„ ์žˆ๊ณ , DB ๋“ฑ์˜ ์˜๊ตฌ ์ €์žฅ์†Œ๊ฐ€ ๋  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

์šฐ๋ฆฌ๊ฐ€ InMemory User๋ฅผ ๋“ฑ๋กํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ–ˆ๋˜ InMemoryUserDetailsManager๋Š” UserDetailsManager ์ธํ„ฐํŽ˜์ด์Šค์˜ ๊ตฌํ˜„์ฒด์ด๊ณ , UserDetailsManager๋Š” UserDetailsService๋ฅผ ์ƒ์†ํ•˜๋Š” ํ™•์žฅ ์ธํ„ฐํŽ˜์ด์Šค

  1. HelloUserDetailsService

    ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ์ธ์ฆ ์ •๋ณด๋กœ ์ธ์ฆ์„ ์ฒ˜๋ฆฌํ•˜๋Š” Custom UserDetailsService

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    
     @Component
     public class HelloUserDetailsServiceV1 implements UserDetailsService {   // (1)
         private final MemberRepository memberRepository;
         private final HelloAuthorityUtils authorityUtils;
        
         // (2)
         public HelloUserDetailsServiceV1(MemberRepository memberRepository, HelloAuthorityUtils authorityUtils) {
             this.memberRepository = memberRepository;
             this.authorityUtils = authorityUtils;
         }
        
         // (3)
         @Override
         public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
             Optional<Member> optionalMember = memberRepository.findByEmail(username);
             Member findMember = optionalMember.orElseThrow(() -> new BusinessLogicException(ExceptionCode.MEMBER_NOT_FOUND));
        
             // (4)
             Collection<? extends GrantedAuthority> authorities = authorityUtils.createAuthorities(findMember.getEmail());
        
             // (5) ๊ฐœ์„ ํ•˜๋ฉด ์ข‹์€ ํฌ์ธํŠธ
             return new User(findMember.getEmail(), findMember.getPassword(), authorities);
         }
     }
    
    • (1) : UserDetailsService ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•œ๋‹ค.
    • (2) : ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ User๋ฅผ ์กฐํšŒํ•˜๊ณ , ์กฐํšŒํ•œ User์˜ ๊ถŒํ•œ(Role) ์ •๋ณด๋ฅผ ์ƒ์„ฑํ•˜๊ธฐ ์œ„ํ•ด MemberRepository์™€ HelloAuthorityUtils ํด๋ž˜์Šค๋ฅผ DI ๋ฐ›๋Š”๋‹ค.
    • UserDetailsService ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ implements ํ•˜๋Š” ๊ตฌํ˜„ ํด๋ž˜์Šค๋Š” (3)๊ณผ ๊ฐ™์ด loadUserByUsername(String username)์ด๋ผ๋Š” ์ถ”์ƒ ๋ฉ”์„œ๋“œ๋ฅผ ๊ตฌํ˜„ํ•ด์•ผ ํ•œ๋‹ค.
    • HelloAuthorityUtils๋ฅผ ์ด์šฉํ•ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์กฐํšŒํ•œ ํšŒ์›์˜ ์ด๋ฉ”์ผ ์ •๋ณด๋ฅผ ์ด์šฉํ•ด Role ๊ธฐ๋ฐ˜์˜ ๊ถŒํ•œ ์ •๋ณด(GrantedAuthority) ์ปฌ๋ ‰์…˜์„ ์ƒ์„ฑํ•œ๋‹ค.
    • (4) ์—์„œ ์ƒ์„ฑํ•œ ๊ถŒํ•œ ์ •๋ณด๋ฅผ Spring Security์—์„œ๋Š” ์•„์ง ์•Œ์ง€ ๋ชปํ•˜๊ธฐ ๋•Œ๋ฌธ์— Spring Security์— ์ด ์ •๋ณด๋“ค์„ ์ œ๊ณตํ•ด ์ฃผ์–ด์•ผ ํ•˜๋ฉฐ, (5) ์—์„œ๋Š” UserDetails ์ธํ„ฐํŽ˜์ด์Šค์˜ ๊ตฌํ˜„์ฒด์ธ User ํด๋ž˜์Šค์˜ ๊ฐ์ฒด๋ฅผ ํ†ตํ•ด ์ œ๊ณต
    • (5)์™€ ๊ฐ™์ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์กฐํšŒํ•œ User ํด๋ž˜์Šค์˜ ๊ฐ์ฒด๋ฅผ ๋ฆฌํ„ดํ•˜๋ฉด Spring Security๊ฐ€ ์ด ์ •๋ณด๋ฅผ ์ด์šฉํ•ด ์ธ์ฆ ์ ˆ์ฐจ๋ฅผ ์ˆ˜ํ–‰ํ•œ๋‹ค.

    โญ ์ฆ‰, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ User์˜ ์ธ์ฆ ์ •๋ณด๋งŒ Spring Security์— ๋„˜๊ฒจ์ฃผ๊ณ , ์ธ์ฆ ์ฒ˜๋ฆฌ๋Š” Spring Security๊ฐ€ ๋Œ€์‹ ํ•ด ์ค€๋‹ค

    โญ UserDetails

    UserDetails๋Š” UserDetailsService์— ์˜ํ•ด ๋กœ๋“œ(load)๋˜์–ด ์ธ์ฆ์„ ์œ„ํ•ด ์‚ฌ์šฉ๋˜๋Š” ํ•ต์‹ฌ User ์ •๋ณด๋ฅผ ํ‘œํ˜„ํ•˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค์ด๋‹ค.

    UserDetails ์ธํ„ฐํŽ˜์ด์Šค์˜ ๊ตฌํ˜„์ฒด๋Š” Spring Security์—์„œ ๋ณด์•ˆ ์ •๋ณด ์ œ๊ณต์„ ๋ชฉ์ ์œผ๋กœ ์ง์ ‘ ์‚ฌ์šฉ๋˜์ง€๋Š” ์•Š๊ณ , Authentication ๊ฐ์ฒด๋กœ ์บก์Аํ™”๋˜์–ด ์ œ๊ณต๋œ๋‹ค.


  2. HelloAuthorityUtils

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    
    package com.springboot.auth.utils;
        
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.security.core.GrantedAuthority;
    import org.springframework.security.core.authority.AuthorityUtils;
    import org.springframework.stereotype.Component;
    import java.util.List;
        
    @Component
    public class HelloAuthorityUtils {
        // (1)
        @Value("${mail.address.admin}")
        private String adminMailAddress;
        
        // (2)
        private final List<GrantedAuthority> ADMIN_ROLES = AuthorityUtils.createAuthorityList("ROLE_ADMIN", "ROLE_USER");
        
        // (3)
        private final List<GrantedAuthority> USER_ROLES = AuthorityUtils.createAuthorityList("ROLE_USER");
        
        public List<GrantedAuthority> createAuthorities(String email) {
            // (4)
            if (email.equals(adminMailAddress)) {
                return ADMIN_ROLES;
            }
            return USER_ROLES;
        }
    }
        
    
    • (1)์€ application.yml์— ์ถ”๊ฐ€ํ•œ ํ”„๋กœํผํ‹ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ํ‘œํ˜„์‹

      • ์ด๊ฑธ ๋„ฃ๋Š”๋‹ค๊ณ  ์ž๋™์œผ๋กœ ๊ด€๋ฆฌ์ž๊ถŒํ•œ์˜ ๋ฉ”์ผ์ด ์ƒ์„ฑ๋˜์ง€ ์•Š๋Š”๋‹ค
      • ์ด๊ฑธ ์„ค์ •ํ•˜๋ฉด (4)์—์„œ ๊ฐ€์ž…ํ•œ ์‚ฌ๋žŒ์˜ ์ด๋ฉ”์ผ์„ ๊ฒ€์ฆํ•œ๋‹ค.
        โ‡’ admin์œผ๋กœ ๋„ฃ์–ด๋‘” ์ด๋ฉ”์ผ๊ณผ ๋™์ผํ•œ์ง€, ๋งŒ์•ฝ ๋™์ผํ•˜๋‹ค๋ฉด ADMIN_ROLES๋ฅผ ๋ถ€์—ฌ ,
        ์•„๋‹ˆ๋ผ๋ฉด USER_ROLES โ†’ ์ธ๊ฐ€๋Š” ๋กœ๊ทธ์ธ ํ•˜๊ณ  ๋‚˜์„œ ์ธ๊ฐ€๊ฐ€ ์ด๋ฃจ์–ด์ง.

      โญ ์‹ค๋ฌด์—์„œ๋Š” ๋‹น์—ฐํžˆ ํšŒ์› ๊ฐ€์ž… ์‹œ, ์•„๋ฌด๋Ÿฐ ์ธ์ฆ ์žฅ์น˜๋„ ์—†์ด ์ด๋ฉ”์ผ ์ฃผ์†Œ๋งŒ ์ž…๋ ฅํ•ด์„œ ๊ด€๋ฆฌ์ž ๊ถŒํ•œ์„ ๋ถ€์—ฌํ•˜์ง€๋Š” ์•Š๋Š”๋‹ค.

      ๊ด€๋ฆฌ์ž ๊ถŒํ•œ์€ ์•„๋ฌด๋ฆฌ ๊ฐ•์กฐํ•ด๋„ ์ง€๋‚˜์น˜์ง€ ์•Š์„ ์ •๋„๋กœ ์ค‘์š”!

      ๊ด€๋ฆฌ์ž๋กœ์„œ ๋“ฑ๋กํ•˜๊ธฐ ์œ„ํ•œ ์ถ”๊ฐ€์ ์ธ ์ธ์ฆ ์ ˆ์ฐจ๊ฐ€ ์žˆ์„ ๊ฒƒ


    • (4)์—์„œ List<GrantedAuthority> ๋ฅผ ๋ฆฌํ„ดํ•˜๋ฉด ํšŒ์›์—๊ฒŒ ์ธ๊ฐ€๊ฐ€ ๋ถ€์—ฌ ๋จ.

  3. H2 ์›น ์ฝ˜์†”์—์„œ ๋“ฑ๋กํ•œ ํšŒ์› ์ •๋ณด ํ™•์ธ ๋ฐ ๋กœ๊ทธ์ธ ์ธ์ฆ ํ…Œ์ŠคํŠธ

    • ํšŒ์›๊ฐ€์ž… ๋ฐ ๋กœ๊ทธ์ธ ์ž˜ ๋จ.

    • h2 console ํ™•์ธํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ์ •๋ณด ๋“ค์–ด์˜ด

      PASSWORD ์—ด์„ ๋ณด๋ฉด ํšŒ์› ๊ฐ€์ž… ๋ฉ”๋‰ด์—์„œ ์ž…๋ ฅํ–ˆ๋˜ ํŒจ์Šค์›Œ๋“œ ์ •๋ณด๊ฐ€ ์•”ํ˜ธํ™” ๋˜์–ด์žˆ๋‹ค

5๏ธโƒฃ HelloUserDetailsServiceV2์ƒ์„ฑ

V1์— ์žˆ๋˜ @Component ๋Š” ์‚ญ์ œํ•ด์•ผ ํ•จ. ํ•˜๋‚˜๋งŒ ์žˆ์–ด์•ผ ํ•œ๋‹ค.

UserDetails์˜ ๊ตฌํ˜„ ํด๋ž˜์Šค์ธ User์˜ ๊ฐ์ฒด๋ฅผ ์ง์ ‘์ ์œผ๋กœ ์ƒ์„ฑํ•ด์„œ ๋ฆฌํ„ดํ•˜๋Š” ๋ถ€๋ถ„์„ ๊ฐœ์„ ํ•  ๊ฒƒ.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
package com.springboot.auth;

import com.springboot.exception.BusinessLogicException;
import com.springboot.exception.ExceptionCode;
import com.springboot.member.Member;
import com.springboot.member.MemberRepository;
import com.springboot.utils.HelloAuthorityUtils;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;

import java.util.Collection;
import java.util.List;
import java.util.Optional;

@Component
public class HelloUserDetailsServiceV2 implements UserDetailsService {
    private final MemberRepository memberRepository;
    private final HelloAuthorityUtils helloAuthorityUtils;

    public HelloUserDetailsServiceV2(MemberRepository memberRepository, HelloAuthorityUtils helloAuthorityUtils) {
        this.memberRepository = memberRepository;
        this.helloAuthorityUtils = helloAuthorityUtils;
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //UserDetailsService๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ํ•„์ˆ˜๋กœ ์žˆ์–ด์•ผ ํ•˜๋Š” ๋ฉ”์„œ๋“œ
        Optional<Member> optionalMember = memberRepository.findByEmail(username);
        Member findMember = optionalMember.orElseThrow(()-> new BusinessLogicException(ExceptionCode.MEMBER_NOT_FOUND));

        //List<GrantedAuthority> authorities=helloAuthorityUtils.createAuthorities(findMember.getEmail());
        //return new User(findMember.getEmail(), findMember.getPassword(), authorities);
				return new HelloUserDetails(findMember);
    }
    
    
    //------------------------์ถ”๊ฐ€ํ•œ ์ฝ”๋“œ --------------------------------------

    //์ด๋„ˆ ํด๋ž˜์Šค ์ƒ์„ฑ , ์ธํ„ฐํŽ˜์ด์Šค ๊ตฌํ˜„ํ•˜๋ฉด์„œ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ƒ์†. ์ƒ์†์ด ๋จผ์ €์™€์•ผ ํ•œ๋‹ค.
    private final class HelloUserDetails  extends Member implements UserDetails{

        //์•„๋ž˜ ํด๋ž˜์Šค๋Š” ๋ฉค๋ฒ„๋ฅผ ์ƒ์†ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํ•„๋“œ์— ์—†์–ด๋„ ๋‹ค ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค (Member ๊ฐ€ superํด๋ž˜์Šค์— ํ•ด๋‹น)
        // (private๋กœ ์„ ์–ธ๋œ ๊ฒƒ์„ ์ œ์™ธํ•˜๊ณ , getter setter๋Š” ๋‹ค public ์ด๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ€์ ธ์˜ค๋Š” ๊ฒƒ์ด ๊ฐ€๋Šฅ)
        HelloUserDetails(Member member){
            setMemberId(member.getMemberId());
            setEmail(member.getEmail());
            setFullName(member.getFullName());
            setPassword(member.getPassword());
        }

        @Override
        public Collection<? extends GrantedAuthority> getAuthorities() { //์ธ๊ฐ€์ •๋ณด๋ฅผ ๋งŒ๋“ค์–ด ์ฃผ์–ด์•ผ ํ•จ.
            return helloAuthorityUtils.createAuthorities(this.getEmail());//HelloUserDetails์˜ ์ด๋ฉ”์ผ์„ ๊ฐ€์ ธ์˜ค๋Š” ๊ฒƒ-> this
        }

        @Override
        public String getUsername() {
            return this.getEmail();
        }

//ํฌ๊ฒŒ ์ค‘์š”ํ•˜์ง€ ์•Š์•„์„œ ๋ชจ๋‘ true๋กœ ๋ณ€๊ฒฝ
        @Override
        public boolean isAccountNonExpired() {
            return true;
        }

        @Override
        public boolean isAccountNonLocked() {
            return true;
        }

        @Override
        public boolean isCredentialsNonExpired() {
            return true;
        }

        @Override
        public boolean isEnabled() {
            return true;
        }
    }
}

  • ๊ธฐ์กด์—๋Š” loadUserByUsername() ๋ฉ”์„œ๋“œ์˜ ๋ฆฌํ„ด ๊ฐ’์œผ๋กœ
    new User(findMember.getEmail(), findMember.getPassword(), authorities);์„ ๋ฆฌํ„ดํ–ˆ์ง€๋งŒ
    ๊ฐœ์„ ๋œ ์ฝ”๋“œ์—์„œ๋Š” (1)๊ณผ ๊ฐ™์ด new HelloUserDetails(findMember);๋ผ๋Š”
    Custom UserDetails ํด๋ž˜์Šค์˜ ์ƒ์„ฑ์ž๋กœ findMember๋ฅผ ์ „๋‹ฌ
  • (2)์˜ HelloUserDetails ํด๋ž˜์Šค๋Š” UserDetails ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๊ณ  ์žˆ๊ณ 
    ๋˜ํ•œ Member ์—”ํ‹ฐํ‹ฐ ํด๋ž˜์Šค๋ฅผ ์ƒ์†ํ•˜๊ณ  ์žˆ๋‹ค.
    • loadUserByUsername() ๋ฉ”์„œ๋“œ ๋‚ด๋ถ€์—์„œ User์˜ ๊ถŒํ•œ ์ •๋ณด๋ฅผ ์ƒ์„ฑํ•˜๋Š” ์ฝ”๋“œ๋„ ์•ˆ์œผ๋กœ ๋“ค์–ด๊ฐ.
      โ‡’ Collection<? extends GrantedAuthority> authorities = authorityUtils.createAuthorities(findMember);๋„
      HelloUserDetails ํด๋ž˜์Šค ๋‚ด๋ถ€๋กœ ์ด๋™

๐Ÿ’ก ์ด๋ ‡๊ฒŒ ๊ตฌ์„ฑํ•˜๋ฉด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์กฐํšŒํ•œ ํšŒ์› ์ •๋ณด๋ฅผ Spring Security์˜ User ์ •๋ณด๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๊ณผ์ •๊ณผ User์˜ ๊ถŒํ•œ ์ •๋ณด๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๊ณผ์ •์„ ์บก์Аํ™”ํ•  ์ˆ˜ ์žˆ๋‹ค.

๐Ÿ’ก ๋˜ํ•œ HelloUserDetails ํด๋ž˜์Šค๋Š” Member ์—”ํ‹ฐํ‹ฐ ํด๋ž˜์Šค๋ฅผ ์ƒ์†ํ•˜๊ณ  ์žˆ์œผ๋ฏ€๋กœ HelloUserDetails๋ฅผ ๋ฆฌํ„ด ๋ฐ›์•„ ์‚ฌ์šฉํ•˜๋Š” ์ธก์—์„œ๋Š” ๋‘ ๊ฐœ ํด๋ž˜์Šค์˜ ๊ฐ์ฒด๋ฅผ ๋ชจ๋‘ ๋‹ค ์†์‰ฝ๊ฒŒ ์บ์ŠคํŒ…ํ•ด์„œ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๋‹ค

(2-3)์—์„œ๋Š” HelloAuthorityUtils์˜ createAuthorities() ๋ฉ”์„œ๋“œ๋ฅผ ์ด์šฉํ•ด User์˜ ๊ถŒํ•œ ์ •๋ณด๋ฅผ ์ƒ์„ฑ

์ด ์ฝ”๋“œ๋Š” ๊ธฐ์กด์—๋Š” loadUserByUsername() ๋ฉ”์„œ๋“œ ๋‚ด๋ถ€์— ์žˆ์—ˆ์ง€๋งŒ ์ง€๊ธˆ์€ HelloUserDetails ํด๋ž˜์Šค ๋‚ด๋ถ€์—์„œ ์‚ฌ์šฉ๋˜๋„๋ก ์บก์Аํ™”๋˜์—ˆ๋‹ค.

  • (2-4)์—์„œ๋Š” Spring Security์—์„œ ์ธ์‹ํ•  ์ˆ˜ ์žˆ๋Š” username์„ Member ํด๋ž˜์Šค์˜ email ์ฃผ์†Œ๋กœ ์ฑ„์šฐ๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์—
    getUsername()์˜ ๋ฆฌํ„ด ๊ฐ’์€ null์ผ ์ˆ˜ ์—†๋‹ค.
  • ๊ธฐํƒ€ UserDetails ์ธํ„ฐํŽ˜์ด์Šค์˜ ์ถ”์ƒ ๋ฉ”์„œ๋“œ๋ฅผ ๊ตฌํ˜„ํ•œ ๋ถ€๋ถ„์€ ์ง€๊ธˆ์€ ํฌ๊ฒŒ ์ค‘์š”ํ•˜์ง€ ์•Š์€ ๋ถ€๋ถ„์ด๋ฏ€๋กœ ๋ชจ๋‘ true๊ฐ’์„ ๋ฆฌํ„ดํ•˜๊ฒŒ ํ–ˆ๋‹ค.

6๏ธโƒฃ User์˜ Role์„ DB์—์„œ ๊ด€๋ฆฌํ•˜๊ธฐ

  • ์ผ๋ฐ˜์ ์œผ๋กœ User์˜ ์ธ์ฆ ์ •๋ณด ๊ฐ™์€ ๋ณด์•ˆ๊ณผ ๊ด€๋ จ๋œ ์ •๋ณด๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ฐ™์€ ์˜๊ตฌ ์ €์žฅ์†Œ์— ์•ˆ์ „ํ•˜๊ฒŒ ๋ณด๊ด€
  • but, ํ˜„์žฌ๊นŒ์ง€ User์˜ ๊ถŒํ•œ ์ •๋ณด๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ
    ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์กฐํšŒํ•œ User ์ •๋ณด๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์ฝ”๋“œ์ƒ์—์„œ ์กฐ๊ฑด์— ๋งž๊ฒŒ ์ƒ์„ฑ
  • User์˜ ๊ถŒํ•œ ์ •๋ณด๋ฅผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ณผ์ • ํ•„์š”
    • User์˜ ๊ถŒํ•œ ์ •๋ณด๋ฅผ ์ €์žฅํ•˜๊ธฐ ์œ„ํ•œ ํ…Œ์ด๋ธ” ์ƒ์„ฑ
    • ํšŒ์› ๊ฐ€์ž… ์‹œ, User์˜ ๊ถŒํ•œ ์ •๋ณด(Role)๋ฅผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅํ•˜๋Š” ์ž‘์—…
    • ๋กœ๊ทธ์ธ ์ธ์ฆ ์‹œ, User์˜ ๊ถŒํ•œ ์ •๋ณด๋ฅผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์กฐํšŒํ•˜๋Š” ์ž‘์—…
  1. User์˜ ๊ถŒํ•œ ์ •๋ณด ํ…Œ์ด๋ธ” ์ƒ์„ฑ
    • ๊ถŒํ•œ๊ด€๋ จ ์ •๋ณด๋Š” ์‹ค๋ฌด์—์„œ ์‹ค์ œ๋กœ ํ…Œ์ด๋ธ”์„ ๋”ฐ๋กœ ๋งŒ๋“ค์–ด์„œ ๊ด€๋ฆฌํ•œ๋‹ค.
      (๊ธฐ๋ณธํ‚ค 1๋ฒˆ์€ admin ๋‚˜๋จธ์ง€๋Š” user)
    • ํ•˜๋‚˜์˜ ๋ฉค๋ฒ„๋Š” ํ•˜๋‚˜์˜ ๊ถŒํ•œ? no . ํ•˜๋‚˜์˜ ๊ถŒํ•œ์€ ํ•˜๋‚˜์˜ ๋ฉค๋ฒ„๋งŒ? no โ‡’ ๋‹ค๋Œ€๋‹ค
    • ๊ทธ๋Ÿฌ๋‚˜ ์ผ๋Œ€๋‹ค๋กœ ๊ตฌํ˜„ํ•  ์˜ˆ์ •์ž„, member๋Š” ์Šคํ”„์‹œํ๋ฆฌํ‹ฐ๊ฐ€ ๊ด€๋ฆฌํ•˜๊ธฐ๋•Œ๋ฌธ์— ๋ฉค๋ฒ„์—๊ฒŒ์„œ๋งŒ ๊ด€๊ณ„๊ตฌํ˜„ ํ•˜๋ฉด ๋จ.
    • Member์— ์ฝ”๋“œ ์ถ”๊ฐ€

      1
      2
      
        @ElementCollection(fetch = FetchType.EAGER)
        //๊ถŒํ•œ ๊ด€๋ จ ํ…Œ์ด๋ธ”์— ์„ค์ • -> ์• ๋„ˆํ…Œ์ด์…˜๋งŒ ๋‹ฌ์•„๋„ ์ž๋™์œผ๋กœ ํ…Œ์ด๋ธ”์ด ์ƒ์„ฑ๋จ.
      
      • List, Set ๊ฐ™์€ ์ปฌ๋ ‰์…˜ ํƒ€์ž…์˜ ํ•„๋“œ๋Š” @ElementCollection ์• ๋„ˆํ…Œ์ด์…˜์„ ์ถ”๊ฐ€ํ•˜๋ฉด
        User ๊ถŒํ•œ ์ •๋ณด์™€ ๊ด€๋ จ๋œ ๋ณ„๋„์˜ ์—”ํ‹ฐํ‹ฐ ํด๋ž˜์Šค๋ฅผ ์ƒ์„ฑํ•˜์ง€ ์•Š์•„๋„ ๊ฐ„๋‹จํ•˜๊ฒŒ ๋งคํ•‘ ์ฒ˜๋ฆฌ๊ฐ€ ๋œ๋‹ค.
      • ์ด๋Œ€๋กœ ์‹คํ–‰ํ•˜๋ฉด ์ž๋™์œผ๋กœ ํ…Œ์ด๋ธ” ์ƒ์„ฑ ๋˜์–ด์žˆ๋Š” ๊ฑธ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

  2. ํšŒ์› ๊ฐ€์ž… ์‹œ, User์˜ ๊ถŒํ•œ ์ •๋ณด(Role)๋ฅผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅ

    ๐Ÿ“Œ DBMemberService

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    
     @Transactional
     public class DBMemberService implements MemberService {
         ...
         private final HelloAuthorityUtils authorityUtils;
         ...
         public Member createMember(Member member) {
             verifyExistsEmail(member.getEmail());
             String encryptedPassword = passwordEncoder.encode(member.getPassword());
             member.setPassword(encryptedPassword);
        
             // (1) Role์„ DB์— ์ €์žฅ
             List<String> roles = authorityUtils.createRoles(member.getEmail());
             member.setRoles(roles);
        
             Member savedMember = memberRepository.save(member);
        
             return savedMember;
         }
        
         ...
         ...
     }
        
    

    ๐Ÿ“ŒcreateRoles() ๋ฉ”์„œ๋“œ๊ฐ€ ์ถ”๊ฐ€๋œ HelloAuthorityUtils ํด๋ž˜์Šค์˜ ์ฝ”๋“œ

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    
     package com.springboot.utils;
        
     import org.springframework.beans.factory.annotation.Value;
     import org.springframework.security.core.GrantedAuthority;
     import org.springframework.security.core.authority.AuthorityUtils;
     import org.springframework.security.core.authority.SimpleGrantedAuthority;
     import org.springframework.stereotype.Component;
        
     import java.util.List;
     import java.util.stream.Collectors;
        
     @Component
     public class HelloAuthorityUtils {
         @Value("${mail.address.admin}")
         private String adminMailAddress;
         private final List<GrantedAuthority> ADMIN_ROLES= AuthorityUtils.createAuthorityList("ROLE_ADMIN", "ROLE_USER");
         private final List<GrantedAuthority> USER_ROLES =AuthorityUtils.createAuthorityList("ROLE_USER");
         private final List<String> ADMIN_ROLES_STRING = List.of("ADMIN", "USER");
         private final List<String> USER_ROLES_STRING = List.of("USER");
        
         //๋ฉ”๋ชจ๋ฆฌ์˜ role์„ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ถŒํ•œ ์ •๋ณด ์ƒ์„ฑ
         public List<GrantedAuthority> createAuthorities(String email){
             if(email.equals(adminMailAddress)){
                 return ADMIN_ROLES;
             }else {
                 return USER_ROLES;
             }
         }
        
         //DB์— ์ €์žฅ๋œ Rolse๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ถŒํ•œ ์ •๋ณด ์ƒ์„ฑ
         public List<GrantedAuthority>createAuthorities(List<String> roles){
             List<GrantedAuthority> authorities = roles.stream().map(role-> new SimpleGrantedAuthority("ROLE_"+role))
                     .collect(Collectors.toList());
             return authorities;
         }
        
         //DB์— ์ €์žฅํ•˜๊ธฐ ์œ„ํ•œ ํ˜•ํƒœ์˜ roles๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ฉ”์„œ๋“œ
         public List<String>createRoles(String email) {
             if (email.equals(adminMailAddress)) {
                 return ADMIN_ROLES_STRING;
             } else {
                 return USER_ROLES_STRING;
             }
         }
     }
    

    ๐Ÿ“Œ ๋กœ๊ทธ์ธ ์ธ์ฆ ์‹œ, User์˜ ๊ถŒํ•œ ์ •๋ณด๋ฅผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์กฐํšŒํ•˜๋Š” ์ž‘์—…

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    
     @Component
     public class HelloUserDetailsServiceV3 implements UserDetailsService {
     		...
        		
         private final class HelloUserDetails extends Member implements UserDetails {
             HelloUserDetails(Member member) {
                 setMemberId(member.getMemberId());
                 setFullName(member.getFullName());
                 setEmail(member.getEmail());
                 setPassword(member.getPassword());
                 setRoles(member.getRoles());        // (1)
             }
        
             @Override
             public Collection<? extends GrantedAuthority> getAuthorities() {
                 // (2) DB์— ์ €์žฅ๋œ Role ์ •๋ณด๋กœ User ๊ถŒํ•œ ๋ชฉ๋ก ์ƒ์„ฑ
                 return authorityUtils.createAuthorities(this.getRoles());
             }
             ...
             ...
         }
     }
        
    
    • (1) : HelloUserDetails๊ฐ€ ์ƒ์†ํ•˜๊ณ  ์žˆ๋Š” Member(extends Member)์— ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์กฐํšŒํ•œ List<String> roles๋ฅผ ์ „๋‹ฌ
    • (2) : ๋‹ค์‹œ Member(extends Member)์— ์ „๋‹ฌํ•œ Role ์ •๋ณด๋ฅผ authorityUtils.createAuthorities() ๋ฉ”์„œ๋“œ์˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ „๋‹ฌํ•ด์„œ
      ๊ถŒํ•œ ๋ชฉ๋ก(List<GrantedAuthority>)์„ ์ƒ์„ฑ
    • createAuthorities(List<String> roles) ๋ฉ”์„œ๋“œ๊ฐ€ ์ถ”๊ฐ€๋œ HelloAuthorityUtils ํด๋ž˜์Šค

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      
        ...
              
        @Component
        public class HelloAuthorityUtils {
            @Value("${mail.address.admin}")
            private String adminMailAddress;
              
            private final List<GrantedAuthority> ADMIN_ROLES = AuthorityUtils.createAuthorityList("ROLE_ADMIN", "ROLE_USER");
            private final List<GrantedAuthority> USER_ROLES = AuthorityUtils.createAuthorityList("ROLE_USER");
            private final List<String> ADMIN_ROLES_STRING = List.of("ADMIN", "USER");
            private final List<String> USER_ROLES_STRING = List.of("USER");
              
            // ๋ฉ”๋ชจ๋ฆฌ ์ƒ์˜ Role์„ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ถŒํ•œ ์ •๋ณด ์ƒ์„ฑ.
            public List<GrantedAuthority> createAuthorities(String email) {
                if (email.equals(adminMailAddress)) {
                    return ADMIN_ROLES;
                }
                return USER_ROLES;
            }
              
            // (1) DB์— ์ €์žฅ๋œ Role์„ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ถŒํ•œ ์ •๋ณด ์ƒ์„ฑ
            public List<GrantedAuthority> createAuthorities(List<String> roles) {
               List<GrantedAuthority> authorities = roles.stream()
                       .map(role -> new SimpleGrantedAuthority("ROLE_" + role)) // (2)
                       .collect(Collectors.toList());
               return authorities;
            }
              
            ...
            ...
        }
              
      

      ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ๊ฐ€์ง€๊ณ  ์˜จ Role ๋ชฉ๋ก(List<String> roles)์„ ๊ทธ๋Œ€๋กœ ์ด์šฉํ•ด์„œ ๊ถŒํ•œ ๋ชฉ๋ก(authorities)์„ ๋งŒ๋“ ๋‹ค.

      ๐Ÿ’ก ์ฃผ์˜ํ•ด์•ผ ํ•  ๊ฒƒ : (2)์™€ ๊ฐ™์ด SimpleGrantedAuthority ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•  ๋•Œ ์ƒ์„ฑ์ž ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋„˜๊ฒจ์ฃผ๋Š” ๊ฐ’์ด
      โ€œ USERโ€ ๋˜๋Š” โ€œADMINโ€œ์œผ๋กœ ๋„˜๊ฒจ์ฃผ๋ฉด ์•ˆ ๋˜๊ณ  โ€œROLE_USERโ€ ๋˜๋Š” โ€œROLE_ADMINโ€ ํ˜•ํƒœ๋กœ ๋„˜๊ฒจ์ฃผ์–ด์•ผ ํ•œ๋‹ค

7๏ธโƒฃ Custom AuthenticationProvider๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•

  • Custom UserDetailsService๋ฅผ ์‚ฌ์šฉํ•ด ๋กœ๊ทธ์ธ ์ธ์ฆ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ์‹์€ Spring Security๊ฐ€ ๋‚ด๋ถ€์ ์œผ๋กœ ์ธ์ฆ์„ ๋Œ€์‹  ์ฒ˜๋ฆฌํ•ด ์ฃผ๋Š” ๋ฐฉ์‹
  • Custom AuthenticationProvider๋ฅผ ์ด์šฉํ•ด ์šฐ๋ฆฌ๊ฐ€ ์ง์ ‘ ๋กœ๊ทธ์ธ ์ธ์ฆ์„ ์ฒ˜๋ฆฌํ•  ๊ฒƒ.

    1. HelloUserAuthenticationProvider ํด๋ž˜์Šค
      • AuthenticationProvider๋ฅผ ๊ตฌํ˜„ํ•œ ๊ตฌํ˜„ ํด๋ž˜์Šค๊ฐ€ Spring Bean์œผ๋กœ ๋“ฑ๋ก๋˜์–ด ์žˆ๋‹ค๋ฉด
        ํ•ด๋‹น AuthenticationProvider๋ฅผ ์ด์šฉํ•ด์„œ ์ธ์ฆ์„ ์ง„ํ–‰
        • ๋”ฐ๋ผ์„œ ํด๋ผ์ด์–ธํŠธ ์ชฝ์—์„œ ๋กœ๊ทธ์ธ ์ธ์ฆ์„ ์‹œ๋„ํ•˜๋ฉด ์šฐ๋ฆฌ๊ฐ€ ๊ตฌํ˜„ํ•œ HelloUserAuthenticationProvider๊ฐ€ ์ง์ ‘ ์ธ์ฆ์„ ์ฒ˜๋ฆฌ
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      
       package com.springboot.utils;
              
       import com.springboot.auth.HelloUserDetailsServiceV2;
       import org.springframework.security.authentication.AuthenticationProvider;
       import org.springframework.security.authentication.BadCredentialsException;
       import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
       import org.springframework.security.core.Authentication;
       import org.springframework.security.core.AuthenticationException;
       import org.springframework.security.core.GrantedAuthority;
       import org.springframework.security.core.userdetails.UserDetails;
       import org.springframework.security.core.userdetails.UsernameNotFoundException;
       import org.springframework.security.crypto.password.PasswordEncoder;
       import org.springframework.stereotype.Component;
              
       import java.util.Collection;
       import java.util.Optional;
              
       @Component
       public class HelloUserAuthenticationProvider implements AuthenticationProvider {
           private final HelloUserDetailsServiceV2 helloUserDetailsServiceV2;
           private final PasswordEncoder passwordEncoder;
              
           public HelloUserAuthenticationProvider(HelloUserDetailsServiceV2 helloUserDetailsServiceV2, PasswordEncoder passwordEncoder) {
               this.helloUserDetailsServiceV2 = helloUserDetailsServiceV2;
               this.passwordEncoder = passwordEncoder;
           }
              
           @Override
           public Authentication authenticate(Authentication authentication) throws AuthenticationException {
               UsernamePasswordAuthenticationToken authToken = (UsernamePasswordAuthenticationToken) authentication;
               String username = authToken.getName();
              
               Optional.ofNullable(username).orElseThrow(()-> new UsernameNotFoundException("Invalid User name or User Password"));
                     
              // (2) ํšŒ์›๊ฐ€์ž…์„ ํ•˜์ง€ ์•Š๊ณ  ๋กœ๊ทธ์ธํ•˜๋ฉด ์ƒ๊ธฐ๋Š” ๋ฌธ์ œ์  ๊ฐœ์„ 
               try {
                   UserDetails userDetails = helloUserDetailsServiceV2.loadUserByUsername(username);
                   String password = userDetails.getPassword();
                   //์•”ํ˜ธํ™” ์•ˆ๋˜์–ด์žˆ์Œ
                          
                          
                   verifyCredentials(authToken.getCredentials(), password);
                   //๋กœ๊ทธ์ธ ์ •๋ณด์— ํฌํ•จ๋œ ํŒจ์Šค์›Œ๋“œ์™€ DB์— ์ €์žฅ๋œ ํŒจ์Šค์›Œ๋“œ ์ •๋ณด๊ฐ€ ์ผ์น˜ํ•˜๋Š”์ฆ
                   //verifyCredentials ์—์„œ ์•”ํ˜ธํ™” ์ด๋ฃจ์–ด์ง(๋‹จ๋ฐฉํ–ฅ ์•”ํ˜ธํ™” - ์ผ๋ฐ˜์ ์œผ๋กœ ๋ณต๊ตฌ๊ฐ€ ๋ถˆ๊ฐ€๋Šฅ)
              
                   Collection<? extends GrantedAuthority> authorities = userDetails.getAuthorities();
                   return UsernamePasswordAuthenticationToken.authenticated(username, password, authorities);
               }catch (Exception e){
                   throw new UsernameNotFoundException(e.getMessage());
               }
           }
                  
           //support๋ฉ”์„œ๋“œ๋Š” ์šฐ๋ฆฌ๊ฐ€ ๋งŒ๋“  ํ”„๋กœ๋ฐ”์ด๋”๊ฐ€ User๊ธฐ๋ฐ˜์˜ password ๋ฐฉ์‹์ด๋ผ๊ณ  ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ์— ์•Œ๋ ค์ฃผ๋Š” ๊ฒƒ,
           // ์• ์ดˆ์— ๊ตฌํ˜„๋˜์–ด ์žˆ๋Š” ๋ฉ”์„œ๋“œ๋ผ์„œ ๋ฐฉ์‹์— ๋งž์ถฐ์„œ ์‚ฌ์šฉํ•˜๊ธฐ.
           //supports() ๋ฉ”์„œ๋“œ์˜ ๋ฆฌํ„ด๊ฐ’์ด true์ผ ๊ฒฝ์šฐ, Spring Security๋Š” ํ•ด๋‹น AuthenticationProvider์˜ authenticate() ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•ด์„œ ์ธ์ฆ์„ ์ง„ํ–‰
           @Override
           public boolean supports(Class<?> authentication) {
               return UsernamePasswordAuthenticationToken.class.equals(authentication);
           }
              
           private void verifyCredentials(Object credentials, String password){
               if(!passwordEncoder.matches((String) credentials, password)){
                   //์ธ์ฝ”๋”ฉ ํ•œ๋‹ค์Œ์— ๋‘˜๋‹ค ์•”ํ˜ธํ™”๋ฅผ ํ•œ๋‹ค.(๋‹จ๋ฐฉํ–ฅ ์•”ํ˜ธํ™” - ์ผ๋ฐ˜์ ์œผ๋กœ ๋ณต๊ตฌ๊ฐ€ ๋ถˆ๊ฐ€๋Šฅ)
                   // ๊ฐ™์œผ๋ฉด true, ๋‹ค๋ฅด๋ฉด false
                   throw new BadCredentialsException("Invaild User name or User Password");
               }
           }
       }
              
      
    2. Custom AuthenticationProvider๋ฅผ ์ด์šฉํ•  ๊ฒฝ์šฐ ํšŒ์›๊ฐ€์ž… ์ „ ๋กœ๊ทธ์ธ ํ•˜๋ฉด ๋‚˜ํƒ€๋Š” ๋ฌธ์ œ์ 
      • ํšŒ์›๊ฐ€์ž…์„ ํ•˜์ง€ ์•Š์€ ์ฑ„๋กœ ๋กœ๊ทธ์ธ์„ ํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™์€ ํ™”๋ฉด์ด ๋‚˜ํƒ€๋‚œ๋‹ค.

      • ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ๊ฑฐ์น˜์ง€ ์•Š์•„์„œ BusinessLogicException์„ ์ฒ˜๋ฆฌํ•˜์ง€ ๋ชปํ•จ.
      • filterChain์˜ ๊ฐ€์žฅ ๋๊นŒ์ง€ ์˜ฌ๋ผ๊ฐ„ ํ›„ ์„œ๋ธ”๋ฆฟ์„ ์ง€๋‚˜ ํด๋ผ์ด์–ธํŠธ์˜ ์š”์ฒญ ์ตœ์ƒ๋‹จ์œผ๋กœ ๊ฐ€๊ฒŒ ๋จโ‡’ ์ฆ‰, ํ†ฐ์บฃ์—์„œ ๋ฐœ์ƒํ•œ ์˜ˆ์™ธ์ด๋‹ค

      ๊ฐœ์„ ํ•˜๋ ค๋ฉด .Cusotm AuthenticationProvider์—์„œ Exception์ด ๋ฐœ์ƒํ•  ๊ฒฝ์šฐ, ์ด Exception์„ catch ํ•ด์„œ AuthenticationException์œผ๋กœ rethrow๋ฅผ ํ•ด์ฃผ๋ฉด ๋œ๋‹ค.
      โ‡’
      (2) ์—์„œ try-catch๋กœ ๊ฐ์‹ผ ์ด์œ ๋Š” ์˜ˆ์™ธ๋ฅผ ๋˜์ ธ ๊ฐœ์„ ํ•˜๊ธฐํ•จ

Spring ์นดํ…Œ๊ณ ๋ฆฌ ๋‚ด ๋‹ค๋ฅธ ๊ธ€ ๋ณด๋Ÿฌ๊ฐ€๊ธฐ

Leave a comment