๐ŸŒˆ [Section4] 3. [ Spring Security ] Security ๊ธฐ๋ณธ 2

ํ˜„์ฃผยท2022๋…„ 11์›” 22์ผ
1

bootcamp

๋ชฉ๋ก ๋ณด๊ธฐ
57/71

๐Ÿ“• ์˜ค๋Š˜ ๋ฐฐ์šด ๋‚ด์šฉ!

  • Spring Security์˜ ์›น ์š”์ฒญ ์ฒ˜๋ฆฌ ํ๋ฆ„
  • ์„œ๋ธ”๋ฆฟ ํ•„ํ„ฐ (Servlet Filter)
  • Spring Security์˜ ์ปดํฌ๋„ŒํŠธ๋กœ ๋ณด๋Š” ์ธ์ฆ ์ฒ˜๋ฆฌ ํ๋ฆ„
  • Spring Security์˜ ๊ถŒํ•œ ๋ถ€์—ฌ ํ๋ฆ„

โœ๏ธ Spring Security์˜ ์›น ์š”์ฒญ ์ฒ˜๋ฆฌ ํ๋ฆ„

(1) ์‚ฌ์šฉ์ž๊ฐ€ ๋ณดํ˜ธ๋œ ๋ฆฌ์†Œ์Šค ์š”์ฒญ

(2) ์ธ์ฆ ๊ด€๋ฆฌ์ž ์—ญํ• ์„ ํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์‚ฌ์šฉ์ž์˜ ํฌ๋ฆฌ๋ด์…œ(Credential) ์š”์ฒญ

โœ”๏ธ ์‚ฌ์šฉ์ž์˜ ํฌ๋ฆฌ๋ด์…œ(Credential)
โžœ ํ•ด๋‹น ์‚ฌ์šฉ์ž๋ฅผ ์ฆ๋ช…ํ•˜๊ธฐ ์œ„ํ•œ ๊ตฌ์ฒด์ ์ธ ์ˆ˜๋‹จ
Ex. password

(3) ์‚ฌ์šฉ์ž๊ฐ€ ์ธ์ฆ ๊ด€๋ฆฌ์ž์—๊ฒŒ ํฌ๋ฆฌ๋ด์…œ ์ œ๊ณต

(4) ์ธ์ฆ ๊ด€๋ฆฌ์ž๊ฐ€ ํฌ๋ฆฌ๋ด์…œ ์ €์žฅ์†Œ์—์„œ ์‚ฌ์šฉ์ž์˜ ํฌ๋ฆฌ๋ด์…œ ์กฐํšŒ

(5) ์ธ์ฆ ๊ด€๋ฆฌ์ž๊ฐ€ ์‚ฌ์šฉ์ž๊ฐ€ ์ œ๊ณตํ•œ ํฌ๋ฆฌ๋ด์…œ๊ณผ ํฌ๋ฆฌ๋ด์…œ ์ €์žฅ์†Œ์— ์ €์žฅ๋œ ํฌ๋ฆฌ๋ด์…œ์„ ๋น„๊ตํ•ด ๊ฒ€์ฆ ์ž‘์—… ์ˆ˜ํ–‰

(5-1) ์œ ํšจํ•œ ํฌ๋ฆฌ๋ด์…œ์ด ์•„๋‹ˆ๋ผ๋ฉด Exception throw

(5-2) ์œ ํšจํ•œ ํฌ๋ฆฌ๋ด์…œ์ด๋ผ๋ฉด

(6) ์ ‘๊ทผ ๊ฒฐ์ • ๊ด€๋ฆฌ์ž ์—ญํ• ์„ ํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์‚ฌ์šฉ์ž๊ฐ€ ์ ์ ˆํ•œ ๊ถŒํ•œ์„ ๋ถ€์—ฌ๋ฐ›์•˜๋Š”์ง€ ๊ฒ€์ฆ

(6-1) ์ ์ ˆํ•œ ๊ถŒํ•œ์„ ๋ถ€์—ฌ๋ฐ›์ง€ ๋ชปํ•œ ์‚ฌ์šฉ์ž๋ผ๋ฉด Exception throw

(6-2) ์ ์ ˆํ•œ ๊ถŒํ•œ์„ ๋ถ€์—ฌ ๋ฐ›์€ ์‚ฌ์šฉ์ž๋ผ๋ฉด ๋ณดํ˜ธ๋œ ๋ฆฌ์†Œ์Šค์˜ ์ ‘๊ทผ ํ—ˆ์šฉ


โœ๏ธ ์„œ๋ธ”๋ฆฟ ํ•„ํ„ฐ (Servlet Filter)

  • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์—”๋“œํฌ์ธํŠธ์— ์š”์ฒญ์ด ๋„๋‹ฌํ•˜๊ธฐ ์ „์— ์ค‘๊ฐ„์—์„œ ์š”์ฒญ์„ ๊ฐ€๋กœ์ฑˆ ํ›„ ์–ด๋–ค ์ฒ˜๋ฆฌ๋ฅผ ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ฃผ๋Š” Java ์ œ๊ณต ์ปดํฌ๋„ŒํŠธ
    ( javax.servlet.Filter ์ธํ„ฐํŽ˜์ด์Šค )

  • ์ด ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•œ ์„œ๋ธ”๋ฆฟ ํ•„ํ„ฐ๋Š” ์›น ์š”์ฒญ(request)์„ ๊ฐ€๋กœ์ฑ„ ์ „์ฒ˜๋ฆฌ๋ฅผ ํ•  ์ˆ˜ ์žˆ๊ณ ,
    ์—”๋“œํฌ์ธํŠธ์—์„œ ์š”์ฒญ ์ฒ˜๋ฆฌ๊ฐ€ ๋๋‚œ ํ›„ ๋„๋‹ฌ๋˜๋Š” ์‘๋‹ต(response)์„ ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ์ „๋‹ฌํ•˜๊ธฐ ์ „์— ํ›„์ฒ˜๋ฆฌ๋ฅผ ํ•  ์ˆ˜ ์žˆ์Œ

  • ์„œ๋ธ”๋ฆฟ ํ•„ํ„ฐ๋Š” ํ•˜๋‚˜ ์ด์ƒ์˜ ํ•„ํ„ฐ๋“ค์„ ์—ฐ๊ฒฐํ•ด ํ•„ํ„ฐ ์ฒด์ธ ๊ตฌ์„ฑ ๊ฐ€๋Šฅ

  • ์š”์ฒญ URI path๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ HttpServletRequest ์ฒ˜๋ฆฌ
    โžœ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์š”์ฒญ ์ „์†กํ•˜๋ฉด, ์„œ๋ธ”๋ฆฟ ์ปจํ…Œ์ด๋„ˆ๋Š” ์š”์ฒญ URI์˜ ๊ฒฝ๋กœ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์–ด๋–ค Filter์™€ ์–ด๋–ค Servlet์„ ๋งคํ•‘ํ• ์ง€ ๊ฒฐ์ •

โœ” ์„œ๋ธ”๋ฆฟ ๊ธฐ๋ฐ˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ Servlet Filter ์œ„์น˜

โžœ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์š”์ฒญ ์ „์†กํ•˜๋ฉด ์ œ์ผ ๋จผ์ € Servlet Filter ๊ฑฐ์น˜๊ณ ,
์ดํ›„ Filter์—์„œ ์ฒ˜๋ฆฌ๊ฐ€ ๋ชจ๋‘ ์™„๋ฃŒ๋˜๋ฉด DispatcherServlet์—์„œ ํด๋ผ์ด์–ธํŠธ์˜ ์š”์ฒญ์„ ํ•ธ๋“ค๋Ÿฌ์— ๋งคํ•‘ํ•˜๊ธฐ ์œ„ํ•œ ๋‹ค์Œ ์ž‘์—… ์‹คํ–‰

[์ฐธ๊ณ ] https://docs.oracle.com/javaee/7/api/javax/servlet/Filter.html

โœ”๏ธ ํ•„ํ„ฐ ์ฒด์ธ (Filter Chain)

  • ์—ฌ๋Ÿฌ๊ฐœ์˜ Filter๊ฐ€ ์ฒด์ธ์„ ํ˜•์„ฑํ•˜๊ณ  ์žˆ๋Š” Filter์˜ ๋ฌถ์Œ
    โ €
  • ๊ฐ๊ฐ ํ•„ํ„ฐ๋“ค์ด doFilter() ๋ฉ”์„œ๋“œ ๊ตฌํ˜„ํ•ด์•ผํ•˜๊ณ , doFilter() ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ์„ ํ†ตํ•ด ํ•„ํ„ฐ์ฒด์ธ ํ˜•์„ฑ
    ( ๊ฐ ํ•„ํ„ฐ๋“ค์€ ์ˆœ์„œ์— ๋”ฐ๋ผ ๋™์ž‘ํ•˜๋„๋ก ์ง€์ • ๊ฐ€๋Šฅ )
    โ €
  • Filter ์ธํ„ฐํŽ˜์ด์Šค ๊ตฌํ˜„ํ•˜๊ณ  ์œ„ ๊ทธ๋ฆผ๊ณผ ๊ฐ™์ด ๊ตฌํ˜„ํ•œ๋‹ค๋ฉด,
    ์„œ๋ธ”๋ฆฟ ํ•„ํ„ฐ์—์„œ ์ž‘์„ฑํ•œ ํŠน๋ณ„ํ•œ ์ž‘์—…๋“ค์„ ์ˆ˜ํ–‰ํ•œ ๋’ค,
    HttpServlet์„ ๊ฑฐ์ณ DispatcherServlet์— ์š”์ฒญ์ด ์ „๋‹ฌ๋˜๋ฉฐ,
    ๋ฐ˜๋Œ€๋กœ DispatcherServlet์—์„œ ์ „๋‹ฌํ•œ ์‘๋‹ต์— ๋Œ€ํ•ด ๋˜ํ•œ ํŠน๋ณ„ํ•œ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Œ

โœ”๏ธ ํ•„ํ„ฐ ์ฒด์ธ์˜ ์ˆœ์„œ ์ง€์ •

  • Spring Bean์œผ๋กœ ๋“ฑ๋ก๋˜๋Š” Filter์— @Order ์• ๋„ˆํ…Œ์ด์…˜ ์ถ”๊ฐ€ํ•ด์„œ ์ˆœ์„œ ์ง€์ • ๊ฐ€๋Šฅ
  • Ordered ์ธํ„ฐํŽ˜์ด์Šค ๊ตฌํ˜„ํ•ด์„œ ์ˆœ์„œ ์ง€์ • ๊ฐ€๋Šฅ
  • FilterRegistrationBean ์ด์šฉํ•ด ๋ช…์‹œ์ ์œผ๋กœ ์ˆœ์„œ ์ง€์ • ๊ฐ€๋Šฅ

โœ” Servlet Filter ๊ธฐ๋ณธ ๊ตฌ์กฐ

public class FirstFilter implements Filter {
     // ์ดˆ๊ธฐํ™” ์ž‘์—…
     public void init(FilterConfig filterConfig) throws ServletException {
        โ €
     }
     โ €
     public void doFilter(ServletRequest request,
                          ServletResponse response,
                          FilterChain chain)
                          throws IOException, ServletException {
        // request(ServletRequest)๋ฅผ ์ด์šฉํ•ด ๋‹ค์Œ Filter๋กœ ๋„˜์–ด๊ฐ€๊ธฐ ์ „์ฒ˜๋ฆฌ ์ž‘์—… ์ˆ˜ํ–‰
โ €
        chain.doFilter(request, response);
โ €
        // response(ServletResponse)๋ฅผ ์ด์šฉํ•ด response์— ๋Œ€ํ•œ ํ›„์ฒ˜๋ฆฌ ์ž‘์—… ์ˆ˜ํ–‰
     }
     โ €
     public void destroy() {
        // Filter๊ฐ€ ์‚ฌ์šฉํ•œ ์ž์›์„ ๋ฐ˜๋‚ฉํ•˜๋Š” ์ฒ˜๋ฆฌ
     }
  }
  • init() ๋ฉ”์„œ๋“œ
    โžœ ์ƒ์„ฑํ•œ Filter์— ๋Œ€ํ•œ ์ดˆ๊ธฐํ™” ์ž‘์—… ์ง„ํ–‰

  • doFilter() ๋ฉ”์„œ๋“œ
    โžœ ํ•ด๋‹น Filter๊ฐ€ ์ฒ˜๋ฆฌํ•˜๋Š” ์‹ค์งˆ์ ์ธ ๋กœ์ง ๊ตฌํ˜„

  • destroy() ๋ฉ”์„œ๋“œ
    โžœ Filter๊ฐ€ ์ปจํ…Œ์ด๋„ˆ์—์„œ ์ข…๋ฃŒ๋  ๋•Œ ํ˜ธ์ถœ
    โžœ ์ฃผ๋กœ Filter๊ฐ€ ์‚ฌ์šฉํ•œ ์ž์›์„ ๋ฐ˜๋‚ฉํ•˜๋Š” ์ฒ˜๋ฆฌ ๋“ฑ์˜ ๋กœ์ง์„ ์ž‘์„ฑํ•˜๊ณ ์ž ํ•  ๋•Œ ์‚ฌ์šฉ

โœ” Spring Security์˜ Filter Chain

( โฌ†๏ธ ์„œ๋ธ”๋ฆฟ ํ•„ํ„ฐ์— Spring Security Filter๊ฐ€ ์ถ”๊ฐ€๋œ ๋ชจ์Šต )

  • Spring Security์—์„œ ๋ณด์•ˆ์„ ์œ„ํ•œ ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•˜๋Š” ํ•„ํ„ฐ์˜ ๋ชจ์Œ

  • Spring Security์˜ Filter Chain์€ URL ๋ณ„๋กœ ์—ฌ๋Ÿฌ๊ฐœ ๋“ฑ๋ก ๊ฐ€๋Šฅ

  • DelegatingFilterProxy / FilterChainProxy ํด๋ž˜์Šค๋Š” Filter ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„
    โžœ ์„œ๋ธ”๋ฆฟ ํ•„ํ„ฐ๋กœ์จ์˜ ์—ญํ• ์„ ํ•จ
    ( ์ด๋ฆ„๋งŒ ์กฐ๊ธˆ ๋‹ค๋ฅผ๋ฟ )

โœ”๏ธ DelegatingFilterProxy

  • Bean์œผ๋กœ ๋“ฑ๋ก๋œ Spring Security์˜ ํ•„ํ„ฐ ์‚ฌ์šฉ ์‹œ์ž‘์ 
    ( ์„œ๋ธ”๋ฆฟ ํ•„ํ„ฐ์™€ ์—ฐ๊ฒฐ๋˜๋Š” Spring Security๋งŒ์˜ ํ•„ํ„ฐ๋ฅผ ApplicationContext์— Bean์œผ๋กœ ๋“ฑ๋กํ•œ ํ›„, ์ด Bean๋“ค์„ ์ด์šฉํ•ด์„œ ๋ณด์•ˆ๊ณผ ๊ด€๋ จ๋œ ์—ฌ๋Ÿฌ๊ฐ€์ง€ ์ž‘์—…๋“ค์„ ์ฒ˜๋ฆฌ )

  • <์„œ๋ธ”๋ฆฟ ์ปจํ…Œ์ด๋„ˆ ์˜์—ญ์˜ ํ•„ํ„ฐ>์™€ <ApplicationContext์— Bean์œผ๋กœ ๋“ฑ๋ก๋œ ํ•„ํ„ฐ๋“ค>์„ ์—ฐ๊ฒฐํ•ด์ฃผ๋Š” ๋ธŒ๋ฆฟ์ง€ ์—ญํ• 
    ( ๋ณด์•ˆ ๊ด€๋ จ X )

โœ”๏ธ FilterChainProxy

  • Spring Security์˜ Filter๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•œ ์‹œ์ž‘์ 
    โžœ FilterChainProxy๋ถ€ํ„ฐ Spring Seucrity์—์„œ ์ œ๊ณตํ•˜๋Š” ๋ณด์•ˆ ํ•„ํ„ฐ๋“ค์ด ํ•„์š”ํ•œ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•จ

  • Servlet Filter๋“ค์€ Bean์œผ๋กœ ๋“ฑ๋ก์ด ์•ˆ๋˜์ง€๋งŒ, Security Filter๋“ค์€ ๋ชจ๋‘ Bean์œผ๋กœ ๋“ฑ๋ก์ด ๋จ

  • Filter Chain์ด ์žˆ์„ ๋•Œ ์–ด๋–ค Filter Chain์„ ์‚ฌ์šฉํ• ์ง€ ๊ฒฐ์ •
    โžœ ๊ฐ€์žฅ ๋จผ์ € ๋งค์นญ๋œ Filter Chain ์‹คํ–‰

    Ex.

    • /api/** ํŒจํ„ด์˜ Filter Chain์ด ์žˆ๊ณ , /api/message URL ์š”์ฒญ ์ „์†กํ•˜๋Š” ๊ฒฝ์šฐ
      โ €
      โžœ ๋””ํดํŠธ ํŒจํ„ด์ธ /**๋„ ์ผ์น˜ํ•˜์ง€๋งŒ,
      /api/** ํŒจํ„ด๊ณผ ์ œ์ผ ๋จผ์ € ๋งค์นญ๋˜๋ฏ€๋กœ, ๊ฐ€์žฅ ๋จผ์ € ๋งค์นญ๋˜๋Š” /api/** ํŒจํ„ด๊ณผ ์ผ์น˜ํ•˜๋Š” Filter Chain๋งŒ ์‹คํ–‰

    • /message/** ํŒจํ„ด์˜ Filter Chain์ด ์—†๋Š”๋ฐ, /message/ URL ์š”์ฒญ์„ ์ „์†กํ•˜๋Š” ๊ฒฝ์šฐ
      โ €
      โžœ ๋งค์นญ๋˜๋Š” Filter Chain์ด ์—†์œผ๋ฏ€๋กœ, ๋””ํดํŠธ ํŒจํ„ด์ธ /** ํŒจํ„ด์˜ Filter Chain ์‹คํ–‰

โœ”๏ธ Spring Security์—์„œ ์ง€์›ํ•˜๋Š” Filter ์ข…๋ฅ˜

  • ์•„์ฃผ ๋งŽ์ง€๋งŒ, ํ•ญ์ƒ ๋ชจ๋“  Filter๊ฐ€ ์ˆ˜ํ–‰๋˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ํ”„๋กœ์ ํŠธ ๊ตฌ์„ฑ ๋ฐ ์„ค์ •์— ๋”ฐ๋ผ ์ผ๋ถ€์˜ Filter๋งŒ ํ™œ์„ฑํ™” ๋˜์–ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ง์ ‘์ ์œผ๋กœ ๊ฐœ๋ฐœ์ž๊ฐ€ ํ•ธ๋“ค๋งํ•  ํ•„์š”๊ฐ€ ์—†๋Š” Filter๋“ค์ด ๋Œ€๋ถ€๋ถ„
    โ €
    โžœ ๊ฐœ๋ฐœ์ž๊ฐ€ Custom Filter๋ฅผ ์ž‘์„ฑํ•˜๊ณ  ๋“ฑ๋กํ•  ๊ฒฝ์šฐ ๊ธฐ์กด ํ•„ํ„ฐ๋“ค ์‚ฌ์ด์—์„œ ์šฐ์„  ์ˆœ์œ„๋ฅผ ์ ์šฉํ•ด ์ˆ˜ํ–‰๋˜์–ด์•ผํ•  ํ•„์š”๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ์— ์ฐธ๊ณ ํ•ด์„œ ์ ์šฉํ•˜๋ฉด ๋จ

[์ฐธ๊ณ ]
https://docs.spring.io/spring-security/reference/servlet/architecture.html#servlet-security-filters


โœ๏ธ Spring Security์˜ ์ปดํฌ๋„ŒํŠธ๋กœ ๋ณด๋Š” ์ธ์ฆ(Authentication) ์ฒ˜๋ฆฌ ํ๋ฆ„

( Spring Security๋Š” Filter๋กœ ์‹œ์ž‘ํ•ด์„œ Filter๋กœ ๋๋‚œ๋‹ค ! )

( โฌ†๏ธ ๋กœ๊ทธ์ธ์— ๋Œ€ํ•œ ์ธ์ฆ ์ฒ˜๋ฆฌ ํ๋ฆ„ )

(1) ์‚ฌ์šฉ์ž๊ฐ€ ๋กœ๊ทธ์ธ ํผ ๋“ฑ์œผ๋กœ Spring Security๊ฐ€ ์ ์šฉ๋œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— request(Username/Password) ์ „์†ก

  • Filter Chain๊นŒ์ง€ ๋“ค์–ด์˜ค๋ฉด UsernamePasswordAuthenticationFilter๊ฐ€ ํ•ด๋‹น ์š”์ฒญ์„ ์ „๋‹ฌ ๋ฐ›์Œ

(2) ๋กœ๊ทธ์ธ ์š”์ฒญ์„ ๋ฐ›์€ UsernamePasswordAuthenticationFilter๊ฐ€ request์˜ ์ •๋ณด(Username/Password)๋ฅผ ์ด์šฉํ•˜์—ฌ UsernamePasswordAuthenticationToken ์ƒ์„ฑ ํ›„,
Authentication์„ UsernamePasswordAuthenticationFilter์—๊ฒŒ ๋‹ค์‹œ ์ „๋‹ฌ

  • UsernamePasswordAuthenticationToken
    โžœ Authentication ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•œ ๊ตฌํ˜„ ํด๋ž˜์Šค

  • ์—ฌ๊ธฐ์„œ Authentication์€ ์•„์ง ์ธ์ฆ์ด ๋˜์ง€ ์•Š์Œ

(3) UsernamePasswordAuthenticationFilter๊ฐ€ ํ•ด๋‹น Authentication์„ AuthenticationManager์—๊ฒŒ ์ „๋‹ฌ

  • AuthenticationManager
    โžœ ์ธ์ฆ ์ฒ˜๋ฆฌ๋ฅผ ์ด๊ด„ํ•˜๋Š” ๋งค๋‹ˆ์ € ์—ญํ• ์„ ํ•˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค

  • ProviderManager
    โžœ AuthenticationManager๋ฅผ ๊ตฌํ˜„ํ•œ ๊ตฌํ˜„ ํด๋ž˜์Šค
    โžœ ProviderManager implements AuthenticationManager

(4) ProviderManager๊ฐ€ AuthenticationProvider์—๊ฒŒ Authentication ์ „๋‹ฌ

(5) Authentication ์ „๋‹ฌ๋ฐ›์€ AuthenticationProvider๊ฐ€ UserDetailsService๋ฅผ ์ด์šฉํ•ด UserDetails ์กฐํšŒ

โœ”๏ธ UserDetails
๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋“ฑ์˜ ์ €์žฅ์†Œ์— ์ €์žฅ๋œ ์‚ฌ์šฉ์ž์˜ Username + ์‚ฌ์šฉ์ž์˜ ์ž๊ฒฉ์„ ์ฆ๋ช…ํ•ด์ฃผ๋Š” ํฌ๋ฆฌ๋ด์…œ(Credential)์ธ Password + ์‚ฌ์šฉ์ž์˜ ๊ถŒํ•œ ์ •๋ณด๋ฅผ ํฌํ•จํ•˜๊ณ  ์žˆ๋Š” ์ปดํฌ๋„ŒํŠธ

  • ์ด UserDetails๋ฅผ ์ œ๊ณตํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ฐ”๋กœ UserDetailsService

(6) UserDetailsServic๊ฐ€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋“ฑ์˜ ์ €์žฅ์†Œ์—์„œ ์‚ฌ์šฉ์ž์˜ ํฌ๋ฆฌ๋ด์…œ(Credential)์„ ํฌํ•จํ•œ ์‚ฌ์šฉ์ž์˜ ์ •๋ณด ์กฐํšŒ

(7) ์กฐํšŒํ•œ ์‚ฌ์šฉ์ž์˜ ํฌ๋ฆฌ๋ด์…œ + ์ •๋ณด ๊ธฐ๋ฐ˜์œผ๋กœ UserDetails ์ƒ์„ฑ

(8) ์ƒ์„ฑ๋œ UserDetails๋ฅผ ๋‹ค์‹œ AuthenticationProvider์—๊ฒŒ ์ „๋‹ฌ

(9) UserDetails๋ฅผ ์ „๋‹ฌ ๋ฐ›์€ AuthenticationProvider๊ฐ€ PasswordEncoder๋ฅผ ์ด์šฉํ•ด <UserDetails์— ํฌํ•จ๋œ ์•”ํ˜ธํ™” ๋œ Password>์™€ <์ธ์ฆ์„ ์œ„ํ•œ Authentication ์•ˆ์— ํฌํ•จ๋œ Password>๊ฐ€ ์ผ์น˜ํ•˜๋Š”์ง€ ๊ฒ€์ฆ ํ›„,

  • ๊ฒ€์ฆ ์„ฑ๊ณต ์‹œ โžœ UserDetails๋ฅผ ์ด์šฉํ•ด ์ธ์ฆ๋œ Authentication ์ƒ์„ฑ

  • ๊ฒ€์ฆ ์‹คํŒจ ์‹œ โžœ Exception ๋ฐœ์ƒ์‹œํ‚ค๊ณ  ์ธ์ฆ ์ฒ˜๋ฆฌ ์ค‘๋‹จ

(10) AuthenticationProvider๊ฐ€ ์ธ์ฆ๋œ Authentication์„ ProviderManager์—๊ฒŒ ์ „๋‹ฌ

  • ์—ฌ๊ธฐ์„œ Authentication์€ ์ธ์ฆ์ด ๋˜์–ด, ์ธ์ฆ์— ์„ฑ๊ณตํ•œ ์‚ฌ์šฉ์ž์˜ ์ •๋ณด(Principal/Credential/GrantedAuthorities) ๊ฐ€์ง€๊ณ  ์žˆ์Œ

(11) ProviderManager๊ฐ€ ์ธ์ฆ๋œ Authentication์„ ๋‹ค์‹œ UsernamePasswordAuthenticationFilter์—๊ฒŒ ์ „๋‹ฌ

  • But, Credential์€ ์œ ์ถœ๋˜๋ฉด ์•ˆ๋˜๋Š” ๊ฐœ์ธ์ •๋ณด์ด๊ธฐ ๋•Œ๋ฌธ์— ๋‚ด๋ถ€์ ์œผ๋กœ ์‚ญ์ œํ•˜๊ณ  ์ „๋‹ฌํ•จ

(12) ์ธ์ฆ๋œ Authentication์„ ์ „๋‹ฌ ๋ฐ›์€ UsernamePasswordAuthenticationFilter๊ฐ€ SecurityContextHolder๋ฅผ ์ด์šฉํ•ด SecurityContext์— ์ธ์ฆ๋œ Authentication ์ €์žฅ

  • Principal / Credential / Collection< GrantedAuthority > ์„ธ๊ฐœ์˜ ํ•„๋“œ๋กœ ๋‚˜๋‰˜์–ด ์ €์žฅ๋จ

  • Credential ํ•„๋“œ๋Š” ๊ฐœ์ธ์ •๋ณด๊ฐ€ ์‚ญ์ œ๋˜๊ณ  ์ „๋‹ฌ๋˜๊ธฐ ๋•Œ๋ฌธ์— null๊ฐ’์ž„

  • SecurityContext๋Š” Spring Security์˜ ์„ธ์…˜ ์ •์ฑ…์— ๋”ฐ๋ผ์„œ HttpSession ์— ์ €์žฅ๋˜์–ด ์‚ฌ์šฉ์ž์˜ ์ธ์ฆ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•˜๊ธฐ๋„ ํ•˜๊ณ , HttpSession์„ ์ƒ์„ฑํ•˜์ง€ ์•Š๊ณ  ๋ฌด์ƒํƒœ๋ฅผ ์œ ์ง€ํ•˜๊ธฐ๋„ ํ•จ

[์ฐธ๊ณ ]
https://docs.spring.io/spring-security/reference/servlet/authentication/architecture.html

โœ” ์ธ์ฆ ์ฒ˜๋ฆฌ ํ๋ฆ„์—์„œ ์‚ฌ์šฉ๋œ Spring Security ์ธ์ฆ ์ปดํฌ๋„ŒํŠธ

โœ… UsernamePasswordAuthenticationFilter

  • ์‚ฌ์šฉ์ž์˜ ๋กœ๊ทธ์ธ request๋ฅผ ์ œ์ผ ๋จผ์ € ๋งŒ๋‚˜๋Š” ์ปดํฌ๋„ŒํŠธ

  • ๋กœ๊ทธ์ธ ํผ์—์„œ ์ œ์ถœ๋˜๋Š” Username๊ณผ Password๋ฅผ ํ†ตํ•œ ์ธ์ฆ์„ ์ฒ˜๋ฆฌํ•˜๋Š” Filter

  • ํด๋ผ์ด์–ธํŠธ๋กœ๋ถ€ํ„ฐ ์ „๋‹ฌ ๋ฐ›์€ Username/Password๋ฅผ Spring Security๊ฐ€ ์ธ์ฆ ํ”„๋กœ์„ธ์Šค์—์„œ ์ด์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก UsernamePasswordAuthenticationToken ์ƒ์„ฑ

๋‚ด๋ถ€๋ฅผ ์‚ดํŽด๋ณด๋ฉด,

public class UsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter { // (1)
โ €
	public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username"; // (2)
โ €
	public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password"; // (3)
โ €
	private static final AntPathRequestMatcher DEFAULT_ANT_PATH_REQUEST_MATCHER = new AntPathRequestMatcher("/login","POST"); // (4)
โ €
  ...
  ...
โ €
	public UsernamePasswordAuthenticationFilter(AuthenticationManager authenticationManager) {
		super(DEFAULT_ANT_PATH_REQUEST_MATCHER, authenticationManager); // (5)
	}
โ €
  // (6)
	@Override
	public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
			throws AuthenticationException {
    // (6-1)
		if (this.postOnly && !request.getMethod().equals("POST")) {
			throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
		}
โ €โ €
		String username = obtainUsername(request);
    ...
โ €
		String password = obtainPassword(request);
    ...
	โ €	
    // (6-2)
    UsernamePasswordAuthenticationToken authRequest = UsernamePasswordAuthenticationToken.unauthenticated(username, password);
		...
โ €โ €
		return this.getAuthenticationManager().authenticate(authRequest); // (6-3)
	}
โ €
	...
  ...
}

โ €
โžœ (1)๊ณผ ๊ฐ™์ด AbstractAuthenticationProcessingFilter ์ƒ์†
UsernamePasswordAuthenticationFilter ํด๋ž˜์Šค์˜ ์ด๋ฆ„์ด Filter๋กœ ๋๋‚˜์ง€๋งŒ doFilter() ๋ฉ”์„œ๋“œ ์กด์žฌ X
But, doFilter() ๋ฉ”์„œ๋“œ๋Š” ๊ผญ ์žˆ์–ด์•ผ ํ•จ
-> ์ƒ์œ„ ํด๋ž˜์Šค์ธ AbstractAuthenticationProcessingFilter ํด๋ž˜์Šค๊ฐ€ doFilter() ๋ฉ”์„œ๋“œ๋ฅผ ํฌํ•จํ•˜๊ณ  ์žˆ์Œ
-> ๊ฒฐ๊ณผ์ ์œผ๋กœ ๋กœ๊ทธ์ธ request๋ฅผ ์ œ์ผ ๋จผ์ € ์ „๋‹ฌ ๋ฐ›๋Š” ํด๋ž˜์Šค๋Š” UsernamePasswordAuthenticationFilter์˜ ์ƒ์œ„ ํด๋ž˜์Šค์ธ AbstractAuthenticationProcessingFilter ํด๋ž˜์Šค์ธ ๊ฒƒ
โ €
โžœ (2)์™€ (3)์„ ํ†ตํ•ด ํด๋ผ์ด์–ธํŠธ์˜ ๋กœ๊ทธ์ธ ํผ์„ ํ†ตํ•ด ์ „์†ก๋˜๋Š” request parameter์˜ ๋””ํดํŠธ name์€ username๊ณผ password๋ผ๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ
โ €
โžœ (4)์˜ AntPathRequestMatcher๋Š” ํด๋ผ์ด์–ธํŠธ์˜ URL์— ๋งค์น˜๋˜๋Š” ๋งค์ฒ˜
-> (4)๋ฅผ ํ†ตํ•ด ํด๋ผ์ด์–ธํŠธ์˜ URL์ด "/login"์ด๊ณ , HTTP Method๊ฐ€ POST์ผ ๊ฒฝ์šฐ ๋งค์น˜ ๋ ๊ฑฐ๋ผ๋Š” ์‚ฌ์‹ค ์˜ˆ์ธก ๊ฐ€๋Šฅ


โœ๏ธ Spring Security์˜ ๊ถŒํ•œ ๋ถ€์—ฌ ์ฒ˜๋ฆฌ ํ๋ฆ„

( ์‚ฌ์šฉ์ž ์ธ์ฆ ํ›„, ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ฆฌ์†Œ์Šค์— ์ ‘๊ทผ ๊ถŒํ•œ์„ ๋ถ€์—ฌํ•˜๋Š” ํ๋ฆ„ )

(1) AuthorizationFilter๊ฐ€ SecurityContextHolder๋กœ ๋ถ€ํ„ฐ Authentication ํš๋“

  • AuthorizationManager
    • ๊ถŒํ•œ ๋ถ€์—ฌ ์ฒ˜๋ฆฌ๋ฅผ ์ด๊ด„ํ•˜๋Š” ๋งค๋‹ˆ์ € ์—ญํ• ์„ ํ•˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค

(2) SecurityContextHolder๋กœ ๋ถ€ํ„ฐ ํš๋“ํ•œ Authentication + HttpServletRequest๋ฅผ AuthorizationManager์—๊ฒŒ ์ „๋‹ฌ

  • RequestMatcherDelegatingAuthorizationManager

    • AuthorizationManager๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๊ตฌํ˜„์ฒด ์ค‘ ํ•˜๋‚˜

    • RequestMatcher ํ‰๊ฐ€์‹์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•ด๋‹น ํ‰๊ฐ€์‹์— ๋งค์น˜๋˜๋Š” AuthorizationManager์—๊ฒŒ ๊ถŒํ•œ ๋ถ€์—ฌ ์ฒ˜๋ฆฌ๋ฅผ ์œ„์ž„ํ•˜๋Š” ์—ญํ• 
      ( ์ง์ ‘ ๊ถŒํ•œ ๋ถ€์—ฌ ์ฒ˜๋ฆฌ X )

(3) RequestMatcherDelegatingAuthorizationManager ๋‚ด๋ถ€์— ๋งค์น˜๋˜๋Š” AuthorizationManager ๊ตฌํ˜„ ํด๋ž˜์Šค๊ฐ€ ์žˆ๋‹ค๋ฉด,
ํ•ด๋‹น AuthorizationManager ๊ตฌํ˜„ ํด๋ž˜์Šค๊ฐ€ ์‚ฌ์šฉ์ž์˜ ๊ถŒํ•œ์„ ์ฒดํฌ

(4) ์ ์ ˆํ•œ ๊ถŒํ•œ์ด๋ผ๋ฉด โžœ ๋‹ค์Œ ์š”์ฒญ ํ”„๋กœ์„ธ์Šค๋ฅผ ๊ณ„์† ์ด์–ด๊ฐ
( Filter ๋‹ค ๋๋‚˜๋ฉด DispatcherServlet์œผ๋กœ ๋„˜์–ด๊ฐ )

(5) ์ ˆํ•œ ๊ถŒํ•œ์ด ์•„๋‹ˆ๋ผ๋ฉด โžœ AccessDeniedException์ด throw๋˜๊ณ , ExceptionTranslationFilter๊ฐ€ AccessDeniedException ์ฒ˜๋ฆฌ

โœ” ๊ถŒํ•œ ๋ถ€์—ฌ ํ๋ฆ„์—์„œ ์‚ฌ์šฉ๋œ Spring Security ๊ถŒํ•œ ๋ถ€์—ฌ ์ปดํฌ๋„ŒํŠธ

โœ… AuthorizationFilter

  • URL์„ ํ†ตํ•ด ์‚ฌ์šฉ์ž์˜ ์•ก์„ธ์Šค๋ฅผ ์ œํ•œํ•˜๋Š” ๊ถŒํ•œ ๋ถ€์—ฌ Filter

  • Spring Security 5.5 ๋ฒ„์ „๋ถ€ํ„ฐ FilterSecurityInterceptor๋ฅผ ๋Œ€์ฒด

๋‚ด๋ถ€๋ฅผ ์‚ดํŽด๋ณด๋ฉด,

public class AuthorizationFilter extends OncePerRequestFilter {
โ €
	private final AuthorizationManager<HttpServletRequest> authorizationManager;
  โ €
โ €  ...
  ...
โ €	
  // (1)
	public AuthorizationFilter(AuthorizationManager<HttpServletRequest> authorizationManager) {
		Assert.notNull(authorizationManager, "authorizationManager cannot be null");
		this.authorizationManager = authorizationManager;
	}
โ €
	@Override
	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {
โ €
		AuthorizationDecision decision = this.authorizationManager.check(this::getAuthentication, request); // (2)
โ €		this.eventPublisher.publishAuthorizationEvent(this::getAuthentication, request, decision);
		if (decision != null && !decision.isGranted()) {
			throw new AccessDeniedException("Access Denied");
		}
		filterChain.doFilter(request, response);
	}
  ...
}

โ €
โžœ (1)๊ณผ ๊ฐ™์ด AuthorizationFilter ๊ฐ์ฒด๊ฐ€ ์ƒ์„ฑ๋  ๋•Œ, AuthorizationManager๋ฅผ DI
โ €
โžœ DI ๋ฐ›์€ AuthorizationManager๋ฅผ ํ†ตํ•ด ๊ถŒํ•œ ๋ถ€์—ฌ ์ฒ˜๋ฆฌ ์ง„ํ–‰
โ €
โžœ (2)์™€ ๊ฐ™์ด DI ๋ฐ›์€ AuthorizationManager์˜ check() ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•ด ์ ์ ˆํ•œ ๊ถŒํ•œ ๋ถ€์—ฌ ์—ฌ๋ถ€๋ฅผ ์ฒดํฌ
( check() ๋ฉ”์„œ๋“œ๋Š” AuthorizationManager ๊ตฌํ˜„ ํด๋ž˜์Šค์— ๋”ฐ๋ผ ๊ถŒํ•œ ์ฒดํฌ ๋กœ์ง์ด ๋‹ค๋ฆ„ )

URL ๊ธฐ๋ฐ˜์œผ๋กœ ๊ถŒํ•œ ๋ถ€์—ฌ ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๋Š” AuthorizationFilter๋Š” AuthorizationManager์˜ ๊ตฌํ˜„ ํด๋ž˜์Šค๋กœ RequestMatcherDelegatingAuthorizationManager๋ฅผ ์‚ฌ์šฉ

โœ… AuthorizationManager

  • ๊ถŒํ•œ ๋ถ€์—ฌ ์ฒ˜๋ฆฌ๋ฅผ ์ด๊ด„ํ•˜๋Š” ๋งค๋‹ˆ์ € ์—ญํ• ์„ ํ•˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค

๋‚ด๋ถ€๋ฅผ ์‚ดํŽด๋ณด๋ฉด,

@FunctionalInterface
public interface AuthorizationManager<T> {
  ...
โ €
	@Nullable
	AuthorizationDecision check(Supplier<Authentication> authentication, T object);
โ €
}

โ €
โžœ check() ๋ฉ”์„œ๋“œ ํ•˜๋‚˜๋งŒ ์ •์˜๋˜์–ด ์žˆ์Œ
โ €
โžœ Supplier์™€ ์ œ๋„ˆ๋ฆญ ํƒ€์ž…์˜ ๊ฐ์ฒด๋ฅผ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๊ฐ€์ง

โœ… RequestMatcherDelegatingAuthorizationManager

  • AuthorizationManager์˜ ๊ตฌํ˜„ ํด๋ž˜์Šค ์ค‘ ํ•˜๋‚˜

  • ์ง์ ‘ ๊ถŒํ•œ ๋ถ€์—ฌ ์ฒ˜๋ฆฌ๋ฅผ ์ˆ˜ํ–‰ ํ•˜์ง€ ์•Š๊ณ  RequestMatcher๋ฅผ ํ†ตํ•ด ๋งค์น˜๋˜๋Š” AuthorizationManager ๊ตฌํ˜„ ํด๋ž˜์Šค์—๊ฒŒ ๊ถŒํ•œ ๋ถ€์—ฌ ์ฒ˜๋ฆฌ๋ฅผ ์œ„์ž„

๋‚ด๋ถ€๋ฅผ ์‚ดํŽด๋ณด๋ฉด,

public final class RequestMatcherDelegatingAuthorizationManager implements AuthorizationManager<HttpServletRequest> {
โ €
  ...
  ...
โ €
	@Override
	public AuthorizationDecision check(Supplier<Authentication> authentication, HttpServletRequest request) {
		if (this.logger.isTraceEnabled()) {
			this.logger.trace(LogMessage.format("Authorizing %s", request));
		}
โ €
    // (1)
		for (RequestMatcherEntry<AuthorizationManager<RequestAuthorizationContext>> mapping : this.mappings) {
โ €
			RequestMatcher matcher = mapping.getRequestMatcher(); // (2)
			MatchResult matchResult = matcher.matcher(request);
			if (matchResult.isMatch()) {   // (3)
				AuthorizationManager<RequestAuthorizationContext> manager = mapping.getEntry();
				if (this.logger.isTraceEnabled()) {
					this.logger.trace(LogMessage.format("Checking authorization on %s using %s", request, manager));
				}
				return manager.check(authentication,
						new RequestAuthorizationContext(request, matchResult.getVariables()));
			}
		}
		this.logger.trace("Abstaining since did not find matching RequestMatcher");
		return null;
	}
}

โ €
โžœ (1)์—์„œ check() ๋ฉ”์„œ๋“œ์˜ ๋‚ด๋ถ€์—์„œ ๋ฃจํ”„๋ฅผ ๋Œ๋ฉด์„œ RequestMatcherEntry ์ •๋ณด๋ฅผ ์–ป๊ณ 

โžœ (2)์—์„œ RequestMatcher ๊ฐ์ฒด๋ฅผ ์–ป์Œ
( ์—ฌ๊ธฐ์„œ์˜ RequestMatcher๋Š” SecurityConfiguration์—์„œ .antMatchers("/orders/**").hasRole("ADMIN")์™€ ๊ฐ™์€ ๋ฉ”์„œ๋“œ ์ฒด์ธ ์ •๋ณด๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์ƒ์„ฑ๋จ )
โ €
โžœ (3)์—์„œ MatchResult.isMatch()๊ฐ€ true์ด๋ฉด AuthorizationManager ๊ฐ์ฒด๋ฅผ ์–ป์€ ๋’ค, ์‚ฌ์šฉ์ž์˜ ๊ถŒํ•œ ์ฒดํฌ


๐ŸŒˆ ๋Š๋‚€์ 

๊ทธ๋ž˜๋„ ํ๋ฆ„๊นŒ์ง€๋Š” ์™ธ์šฐ๋ฉด ๊ดœ์ฐฎ๋‹ค !!
์ฝ”๋“œ์—์„œ ๋ณด๊ณ  ํ๋ฆ„์„ ๋Š๋‚„ ์ˆ˜ ์žˆ๋„๋ก ๊ณต๋ถ€ํ•ด์•ผ์ง€

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

comment-user-thumbnail
2023๋…„ 1์›” 5์ผ

๊ทธ๋ฆผ ๊ทธ๋ฆฌ์‹  ๋ถ„ ๋•ก์žก์œผ์…จ๋„ค..

1๊ฐœ์˜ ๋‹ต๊ธ€