Spring Security

이 λ‚΄μš©μ€ Spring Security Architecture 곡식 λ¬Έμ„œμž…λ‹ˆλ‹€. Servlet을 κΈ°λ°˜μœΌλ‘œν•˜λŠ” μ–΄ν”Œλ¦¬μΌ€μ΄μ…˜μ˜ μŠ€ν”„λ§ μ‹œνλ¦¬ν‹° 아킀텍쳐에 λŒ€ν•œ λ‚΄μš©μ„ 닀루고 μžˆμŠ΅λ‹ˆλ‹€.

Architecture

Review of Filters

μŠ€ν”„λ§ μ‹œνλ¦¬ν‹°μ˜ Servlet은 Servlet Filter λ ˆλ²¨μ„ 기반으둜 ν•œλ‹€. μ•„λž˜ μ΄λ―Έμ§€λŠ” HTTP request에 λŒ€ν•œ ν•Έλ“€λŸ¬μ˜ 일반적인 ꡬ쑰λ₯Ό λ‚˜νƒ€λ‚Έλ‹€.

ν΄λΌμ΄μ–ΈνŠΈλŠ” μ–΄ν”Œλ¦¬μΌ€μ΄μ…˜μ˜ requestλ₯Ό 보내고 μ»¨ν…Œμ΄λ„ˆλŠ” Filter와 Servlet을 ν¬ν•¨ν•˜λŠ” FilterChain을 μƒμ„±ν•œλ‹€. 이 λ•Œ request URI κ²½λ‘œμ— κΈ°λ°˜ν•΄ HttpServletRequestλ₯Ό μ²˜λ¦¬ν•œλ‹€. μŠ€ν”„λ§ MVC μ–΄ν”Œλ¦¬μΌ€μ΄μ…˜μ—μ„œ Servlet은 DispatcherServlet의 κ΅¬ν˜„μ²΄μ΄λ‹€.

ν•˜λ‚˜μ˜ Servlet은 기껏해야 1개의 HttpServletRequest와 HttpServletResponseλ₯Ό 닀룬닀. κ·ΈλŸ¬λ‚˜ 2개 μ΄μƒμ˜ Filterλ₯Ό μ‚¬μš©ν•˜μ—¬ λ‹€μŒ μž‘μ—…μ„ μˆ˜ν–‰ν•  수 μžˆλ‹€.

  • λ‹€μŒ Filterκ°€ ν˜ΈμΆœλ˜μ§€ μ•Šλ„λ‘ ν•œλ‹€. 이 경우 FilterλŠ” HttpServletResponseλ₯Ό μž‘μ„±ν•œλ‹€.

  • λ‹€μŒ Filter와 Servletμ—μ„œ μ‚¬μš©λ˜λŠ” HttpServletRequest λ˜λŠ” HttpServletResponseλ₯Ό μˆ˜μ •ν•œλ‹€.

Filter의 νš¨κ³ΌλŠ” 이λ₯Ό ν†΅κ³Όν•˜λŠ” FilterChain에 μžˆλ‹€.

ν•˜λ‚˜μ˜ FilterλŠ” λ‹€μŒ Filter κ΅¬ν˜„μ²΄μ™€ Servletμ—λ§Œ 영ν–₯을 쀄 수 μžˆλ‹€. λ”°λΌμ„œ 각각의 Filterκ°€ μ‚¬μš©λ˜λŠ” μˆœμ„œλŠ” μƒλ‹Ήνžˆ μ€‘μš”ν•˜λ‹€.

DelegatingFilterProxy

μŠ€ν”„λ§μ€ DelegatingFlterProxy λ₯Ό μ œκ³΅ν•œλ‹€. μ΄λŠ” Servlet μ»¨ν…Œμ΄λ„ˆμ™€ μŠ€ν”„λ§μ˜ Application Context 사이λ₯Ό μ—°κ²°ν•΄μ€€λ‹€. Servlet μ»¨ν…Œμ΄λ„ˆλŠ” Servlet μŠ€νŽ™μ΄κΈ° λ•Œλ¬Έμ— μŠ€ν”„λ§μ—μ„œ μ •μ˜λœ λΉˆμ„ μ£Όμž…λ°›μ•„ μ‚¬μš©ν•  수 μ—†λ‹€.

이 λ•Œ DelegatingFilterProxyλ₯Ό 톡해 μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆμ—μ„œ μ‘΄μž¬ν•˜λŠ” νŠΉμ • Bean을 μ°Ύμ•„ μš”μ²­μ„ μœ„μž„ν•œλ‹€.

FilterChainProxy

FilterChainProxy λŠ” μŠ€ν”„λ§ μ‹œνλ¦¬ν‹°μ— μ˜ν•΄ μ œκ³΅λ˜λŠ” νŠΉλ³„ν•œ 필터이닀. SecurityFilterChain을 톡해 λ§Žμ€ ν•„ν„° μΈμŠ€ν„΄μŠ€λ₯Ό μœ„μž„ν•œλ‹€.FilterChainProxy도 Beanμ΄λ―€λ‘œ DelegatingFilterProxy둜 wrappingλœλ‹€.

SecurityFilterChain

SecurityFilterChain은 ν˜„μž¬ requestμ—μ„œ μ–΄λ–€ μŠ€ν”„λ§ μ‹œνλ¦¬ν‹° Filter μΈμŠ€ν„΄μŠ€κ°€ μ‚¬μš©λ˜μ–΄μ•Ό ν• μ§€ κ²°μ •ν•˜κΈ° μœ„ν•΄ FilterChainProxy에 μ˜ν•΄μ„œ μ‚¬μš©λœλ‹€.

SecurityFilterChain μ•ˆμ˜ Security Filter듀은 일반적으둜 Bean이닀. κ·ΈλŸ¬λ‚˜ DelegatingFilterProxy λŒ€μ‹ μ— FilterChainProxy 둜 λ“±λ‘λœλ‹€.

FilterChainProxy λŠ” Servlet μ»¨ν…Œμ΄λ„ˆλ‚˜ DelegatingFilterProxy에 μ§μ ‘μ μœΌλ‘œ λ“±λ‘ν•˜λŠ” 것에 λ§Žμ€ 이점을 μ œκ³΅ν•œλ‹€.

  1. μŠ€ν”„λ§ μ‹œνλ¦¬ν‹° Servlet의 μ‹œμž‘μ μ„ μ œκ³΅ν•œλ‹€. β‡’ trouble shooting에 용이

  2. 보이지 μ•ŠλŠ” μž‘μ—…μ„ μˆ˜ν–‰ν•  수 μžˆλ‹€. (HttpFireWall λ“±)

  3. SecurityFilterChain이 invokeλ˜λŠ” μ‹œμ μ„ κ²°μ •ν•˜λŠ” 데에 μœ μ—°μ„±μ„ μ œκ³΅ν•œλ‹€.

    URL을 기반으둜 λ™μž‘ν•˜λŠ”λ°, RequestMathcer interfaceλ₯Ό μ‚¬μš©ν•¨μœΌλ‘œμ¨ invocation을 κ²°μ •ν•  수 μžˆλ‹€.

μœ„ κ·Έλ¦Όμ—μ„œ FilterChainProxy κ°€ μ–΄λ–€ SecurityFilterChain이 μ‚¬μš©λ μ§€ κ²°μ •ν•œλ‹€. λΆ€ν•©ν•˜λŠ” 첫번째 SecurityFilterChain 만 μ μš©λœλ‹€. λ§Œμ•½ /api/message/ κ°€ μš”μ²­λœλ‹€λ©΄, /api/** νŒ¨ν„΄μ˜ SecurityFilterChain0SecurityFilterChain_0이 λ§€μΉ˜λœλ‹€. λ”°λΌμ„œ 비둝SecurityFilterChainnSecurityFilterChain_n도 λ§€μΉ˜λ μ§€λΌλ„, SecurityFilterChain0SecurityFilterChain_ 0 이 μ‹€ν–‰λœλ‹€.

λ§Œμ•½ /essages/ URL이 μš”μ²­λœλ‹€λ©΄ SecurityFilterChain0SecurityFilterChain_0κ³ΌλŠ” λ§€μΉ˜λ˜μ§€ μ•ŠλŠ”λ‹€. λ”°λΌμ„œ FilterChainProxy λŠ” 각각의 SecurityFiterChain 을 μ‹œλ„ν•΄λ³΄λ‹€κ°€ κ²°κ΅­ SecurityFilterChainnSecurityFilterChain_n이 μ μš©λœλ‹€.

SecurityFilterChain0SecurityFilterChain_0은 3개의 Filter μΈμŠ€ν„΄μŠ€λ‘œ κ΅¬μ„±λ˜μ–΄μžˆκ³ , SecurityFilterChainnSecurityFilterChain_n은 4개의 ν•„ν„° μΈμŠ€ν„΄μŠ€λ‘œ κ΅¬μ„±λ˜μ–΄ μžˆλ‹€. 각각의 SecurityFilterChain 은 νŠΉλ³„ν•˜κ³  λ…λ¦½μ μœΌλ‘œ ꡬ성될 수 μžˆλ‹€. 사싀 SecurityFilterChain 은 λ§Œμ•½ μ–΄ν”Œλ¦¬μΌ€μ΄μ…˜μ΄ Spring Securityκ°€ νŠΉμ • requestλ₯Ό λ¬΄μ‹œν•˜κΈΈ μ›ν•œλ‹€λ©΄, Filter μΈμŠ€ν„΄μŠ€λ₯Ό κ°€μ§€μ§€ μ•Šκ²Œ ν•  μˆ˜λ„ μžˆλ‹€.

Security Filters

Security FilterλŠ” SecurityFilterChain API와 ν•¨κ»˜ FilterChainProxy에 μ‚½μž…λœλ‹€. 이 Filter듀은 인증, 인가 λ“± λ‹€μ–‘ν•œ λͺ©μ μœΌλ‘œ μ‚¬μš©λ  수 μžˆλ‹€. FilterλŠ” μ›ν•˜λŠ” μˆœμ„œλ‘œ μ‹€ν–‰λ˜κΈ° μœ„ν•΄ νŠΉμ •ν•œ μˆœμ„œλ‘œ μ‹€ν–‰λœλ‹€. 예λ₯Ό λ“€μ–΄ 인증을 μˆ˜ν–‰ν•˜λŠ” FilterλŠ” 인가λ₯Ό μˆ˜ν–‰ν•˜λŠ” Filter 전에 μ‹€ν–‰λ˜μ–΄μ•Ό ν•œλ‹€. Spring Security의 Filterλ“€μ˜ μˆœμ„œλ₯Ό μ•„λŠ” 것은 일반적으둜 ν•„μš”ν•œ 것은 μ•„λ‹ˆμ§€λ§Œ, μˆœμ„œλ₯Ό μ•„λŠ” 것이 도움이 λ˜λŠ” λ•Œκ°€ μžˆλ‹€.

https://github.com/spring-projects/spring-security/blob/6.3.1/config/src/main/java/org/springframework/security/config/annotation/web/builders/FilterOrderRegistration.javaarrow-up-right

security configuration의 ν•œκ°€μ§€ 예λ₯Ό λ“€μ–΄λ³΄μž.

μœ„ Configuration은 λ‹€μŒμ˜ Filter μˆœμ„œλ₯Ό κ°€μ§„λ‹€.

  • CsrfFilter ← HttpSecurity#csrf

    • CsrfFilter λŠ” CSRF 곡격을 막기 μœ„ν•΄ μ‹€ν–‰λœλ‹€.

  • UsernamePasswordAuthenticationFilter ← HttpSecurity#formLogin

  • BasicAuthenticationFilter ← HttpSecurity#httpBasic

    • requestλ₯Ό authenticateν•˜κΈ° μœ„ν•΄ μ‹€ν–‰λœλ‹€.

  • AuthorizationFilter ← HttpSecurity#authorizeHttpRequests

    • AuthorizationFilter λŠ” requestλ₯Ό authorize ν•˜κΈ° μœ„ν•΄ μ‹€ν–‰λœλ‹€.

μœ„μ˜ λ‚˜μ™€μžˆλŠ” Filter 외에 λ‹€λ₯Έ Filter μΈμŠ€ν„΄μŠ€κ°€ μžˆμ„ 수 μžˆλ‹€. νŠΉμ • request에 μ‹€ν–‰λ˜λŠ” Filter 리슀트λ₯Ό 보고 μ‹Άλ‹€λ©΄, print ν•΄μ„œ λ³Ό 수 μžˆλ‹€.

Printing the Security Filters

가끔은 νŠΉμ • request에 μ‹€ν–‰λ˜λŠ” Security Filter 리슀트λ₯Ό λ³΄λŠ”κ²Œ μœ μš©ν•  λ•Œλ„ μžˆλ‹€. 예λ₯Ό λ“€μ–΄ μΆ”κ°€ν•œ Filterκ°€ ν™•μ‹€νžˆ μ‹€ν–‰λ˜λŠ”μ§€ ν™•μΈν•˜κ³  μ‹Άλ‹€λ©΄ ν™•μΈν•˜λ©΄ λœλ‹€.

Filter λ¦¬μŠ€νŠΈλŠ” μ–΄ν”Œλ¦¬μΌ€μ΄μ…˜μ΄ 싀행될 λ•Œ INFO λ ˆλ²¨μ—μ„œ ν”„λ¦°νŠΈλœλ‹€. λ”°λΌμ„œ λ‹€μŒκ³Ό 같이 μ½˜μ†”μ—μ„œ 확인할 수 μžˆλ‹€.

λ˜λŠ” 직접 logging을 μ΄μš©ν•΄μ„œ 확인할 μˆ˜λ„ μžˆλ‹€.

Adding a Custom Filter to the Filter Chain

λŒ€λΆ€λΆ„ κΈ°λ³Έ Security Filter듀은 μ–΄ν”Œλ¦¬μΌ€μ΄μ…˜μ— securityλ₯Ό μ œκ³΅ν•˜κΈ°μ— μΆ©λΆ„ν•˜λ‹€. ν•˜μ§€λ§Œ Custom Filterλ₯Ό security filter chain에 μΆ”κ°€ν•˜κ³  싢을 λ•Œκ°€ μ‘΄μž¬ν•  것이닀.

예λ₯Ό λ“€μ–΄, tenant id headerλ₯Ό κ°€μ Έκ°€μ„œ ν˜„μž¬ μœ μ €κ°€ ν•΄λ‹Ή tenant에 접근을 κ°€μ§€κ³  μžˆλŠ”μ§€ ν™•μΈν•˜λŠ” Filterλ₯Ό μΆ”κ°€ν•˜κ³  μ‹Άλ‹€κ³  ν•΄λ³΄μž. μš°λ¦¬λŠ” ν˜„μž¬ μœ μ €λ₯Ό μ•Œ ν•„μš”κ°€ 있기 λ•Œλ¬Έμ— 어디에 Filterλ₯Ό μΆ”κ°€ν•΄μ•Ό ν• μ§€ μ•Œ 수 μžˆλ‹€. μš°λ¦¬λŠ” 인증 Filter 뒀에닀가 μΆ”κ°€ν•΄μ•Ό ν•œλ‹€.

μƒ˜ν”Œ μ½”λ“œλŠ” λ‹€μŒκ³Ό 같이 λ™μž‘ν•œλ‹€.

  1. request headerλ‘œλΆ€ν„° tenant idλ₯Ό μ–»λŠ”λ‹€.

  2. ν˜„μž¬ μœ μ €κ°€ tenant id에 μ ‘κ·Ό κΆŒν•œμ΄ μžˆλŠ”μ§€ κ²€μ‚¬ν•œλ‹€.

  3. μ ‘κ·Ό κΆŒν•œμ„ κ°€μ§€κ³  μžˆλ‹€λ©΄ λ‚˜λ¨Έμ§€ ν•„ν„°λ₯Ό μ‹€ν–‰ν•œλ‹€.

  4. μ ‘κ·Ό κΆŒν•œμ΄ μ—†λ‹€λ©΄ AccessDeniedException 을 λ˜μ§„λ‹€.

Filter λ₯Ό implements ν•˜λŠ” λŒ€μ‹ , OncePerRequestFilter λ₯Ό extends ν•  μˆ˜λ„ μžˆλ‹€. OncePerRequestFilter λŠ” filter의 base class이고, request λ‹Ή 1번만 μ‹€ν–‰λœλ‹€. 그리고 HttpServletRequest 와 HttpServletResponse λ₯Ό νŒŒλΌλ―Έν„°λ‘œ λ°›λŠ” doFilterInternal λ©”μ†Œλ“œλ₯Ό μ œκ³΅ν•œλ‹€.

이제, Filterλ₯Ό security filter chain에 μ μš©ν•΄λ³΄μž.

AuthorizationFilter 전에 TenantFilterλ₯Ό μΆ”κ°€ν•˜κΈ° μœ„ν•΄ HttpSecurity#addFilterBefore λ₯Ό μ‚¬μš©ν•œλ‹€.

이 ν•„ν„°λ₯Ό AythorizationFilter 전에 μΆ”κ°€ν•¨μœΌλ‘œμ¨ TenantFilter κ°€ authentication ν•„ν„° 뒀에 μ‹€ν–‰ν•˜κ²Œ ν–ˆλ‹€. λ˜ν•œ νŠΉμ • ν•„ν„° 뒀에 μƒˆλ‘œμš΄ ν•„ν„°λ₯Ό μΆ”κ°€ν•˜κΈ° μœ„ν•΄ HttpSecurity#addFilterAfter λ₯Ό μ‚¬μš©ν•˜κ±°λ‚˜, νŠΉμ • ν•„ν„° ν¬μ§€μ…˜μ— μƒˆλ‘œμš΄ ν•‰λŸ¬λ₯Ό μΆ”κ°€ν•˜κΈ° μœ„ν•΄ HttpSecurity#addFilterAt 을 μ‚¬μš©ν•  수 μžˆλ‹€.

μ»€μŠ€ν…€ ν•„ν„°λ₯Ό μŠ€ν”„λ§ 빈으둜 μ„ μ–Έν•  λ•Œ μ£Όμ˜ν•΄μ•Ό ν•œλ‹€. 필터에 @Component μ–΄λ…Έν…Œμ΄μ…˜μ„ μ‚¬μš©ν•˜κ±°λ‚˜ configuration에 빈으둜 μ„ μ–Έν•˜κ±°λ‚˜ λ‘˜ 쀑에 ν•˜λ‚˜λ§Œ μ‚¬μš©ν•΄μ•Ό ν•œλ‹€. μŠ€ν”„λ§ λΆ€νŠΈλŠ” μžλ™μ μœΌλ‘œ 이것을 λ‚΄μž₯된 μ»¨ν…Œμ΄λ„ˆμ— λ“±λ‘ν•œλ‹€. λ”°λΌμ„œ ν•„ν„°κ°€ μ»¨ν…Œμ΄λ„ˆμ— μ˜ν•΄ 1번, μŠ€ν”„λ§ μ‹œνλ¦¬ν‹°μ— μ˜ν•΄μ„œ 1번 총 2번 invoke될 수 μžˆλ‹€.

λ§Œμ•½ DI의 이점을 μ–»κΈ° μœ„ν•΄ Spring Bean으둜 ν•„ν„°λ₯Ό λ“±λ‘ν•˜λ©΄μ„œ 쀑볡 invocation을 ν”Όν•˜λ €λ©΄, FilterRegistrationBean 으둜 μ„ μ–Έν•˜κ³  enabled 을 false둜 μ„€μ •ν•¨μœΌλ‘œμ¨ μŠ€ν”„λ§ λΆ€νŠΈκ°€ ν•„ν„°λ₯Ό μ»¨ν…Œμ΄λ„ˆμ— λ“±λ‘ν•˜μ§€ μ•Šκ²Œ ν•  수 μžˆλ‹€.

Handling Security Exceptions

ExceptionTranslationFilter λŠ” AccessDeniedException κ³Ό AuthenticationException 을 HTTP response 으둜 λ³€ν™˜ν•  수 μžˆλ‹€. ExceptionTranslationFilter λŠ” Security Filter 쀑 ν•˜λ‚˜λ‘œ FilterChainProxy에 μ‚½μž…λœλ‹€.

μ•„λž˜ μ΄λ―Έμ§€λŠ” ExceptionTranslationFilter 와 λ‹€λ₯Έ μ»΄ν¬λ„ŒνŠΈμ™€μ˜ 관계λ₯Ό 보여쀀닀.

  1. ExceptionTranslationFilter κ°€ μ–΄ν”Œλ¦¬μΌ€μ΄μ…˜μ˜ λ‚˜λ¨Έμ§€ 뢀뢄을 μ‹€ν–‰ν•˜κΈ° μœ„ν•΄ FilterChain.doFilter(request, response) λ₯Ό μ‹€ν–‰ν•œλ‹€.

  2. λ§Œμ•½ μœ μ €κ°€ μΈμ¦λ˜μ§€ μ•Šμ•˜κ±°λ‚˜ AuthenticationException 인 경우, Authentication을 μ‹œμž‘ν•œλ‹€.

    1. SecurityContextHolderκ°€ μ΄ˆκΈ°ν™”λœλ‹€.

    2. HttpServletRequest λŠ” 인증이 μ„±κ³΅ν•˜λ©΄ original requestλ₯Ό replayν•˜λŠ”λ° μ‚¬μš©ν•  수 μžˆλ„λ‘ μ €μž₯λœλ‹€.

    3. AutenticationEntryPoint λŠ” ν΄λΌμ΄μ–ΈνŠΈλ‘œλΆ€ν„° credential을 μš”μ²­ν•˜κΈ° μœ„ν•΄ μ‚¬μš©λœλ‹€. 예λ₯Ό λ“€μ–΄ 둜그인 νŽ˜μ΄μ§€λ‘œ λ¦¬λ‹€μ΄λ ‰νŠΈν•˜κ±°λ‚˜, WWW-Authenticate 헀더λ₯Ό 보낼 수 μžˆλ‹€.

  3. AccessDeniedException 인 경우, 접근을 μ œν•œν•œλ‹€. AccessDeniedHandler κ°€ 접근을 μ œν•œν•˜κΈ° μœ„ν•΄ μ‹€ν–‰λœλ‹€.

λ§Œμ•½ μ–΄ν”Œλ¦¬μΌ€μ΄μ…˜μ΄ AccessDeniedException μ΄λ‚˜ AuthenticationException 을 μΌμœΌν‚€μ§€ μ•ŠλŠ”λ‹€λ©΄, ExceptionTranslationFilter λŠ” 아무것도 ν•˜μ§€ μ•ŠλŠ”λ‹€.

ExceptionTranslationFilter 의 μ˜μ‚¬μ½”λ“œλŠ” λ‹€μŒκ³Ό 같이 λ‚˜νƒ€λ‚Ό 수 μžˆλ‹€.

  1. FilterChain.doFilter(request, response) λŠ” μ–΄ν”Œλ¦¬μΌ€μ΄μ…˜μ˜ λ‚˜λ¨Έμ§€ 뢀뢄을 μ‹€ν–‰ν•˜λŠ” 것과 κ°™λ‹€. λ§Œμ•½ μ–΄ν”Œλ¦¬μΌ€μ΄μ…˜μ˜ λ‚˜λ¨Έμ§€ 뢀뢄이 AuthenticationException λ˜λŠ” AccessDeniedException 을 λ°œμƒμ‹œν‚¨λ‹€λ©΄, μ—¬κΈ°μ„œ μ²˜λ¦¬λœλ‹€.

  2. λ§Œμ•½ μœ μ €κ°€ μΈμ¦λ˜μ§€ μ•Šμ•˜κ±°λ‚˜ AuthenticationException 인 경우, Authentication을 μ‹œμž‘ν•œλ‹€.

  3. κ·Έλ ‡μ§€ μ•Šλ‹€λ©΄, 접근을 μ œν•œν•œλ‹€.

Saving Requests Between Authentication

requestκ°€ authentication을 κ°€μ§€κ³  μžˆμ§€ μ•Šκ³  authentication을 μš”κ΅¬ν•˜λŠ” λ¦¬μ†ŒμŠ€μΌ λ•Œ, 인증이 μ„±κ³΅ν•œ ν›„ re-requestν•˜κΈ° μœ„ν•΄ 인증된 λ¦¬μ†ŒμŠ€μ— λŒ€ν•œ μš”μ²­μ„ μ €μž₯ν•  ν•„μš”κ°€ μžˆλ‹€. μŠ€ν”„λ§ μ‹œνλ¦¬ν‹°μ—μ„œ 이 것은 RequestCache κ΅¬ν˜„μ²΄λ₯Ό μ‚¬μš©ν•˜λŠ” HttpServletRequest λ₯Ό μ €μž₯ν•¨μœΌλ‘œμ¨ 이뀄진닀.

RequestCache

HttpServletRequest λŠ” RequestCache 에 μ €μž₯λœλ‹€. μœ μ €κ°€ μ„±κ³΅μ μœΌλ‘œ μΈμ¦λ˜μ—ˆμ„ λ•Œ, RequestCache λŠ” μ›λž˜μ˜ requestλ₯Ό replayν•œλ‹€. RequestCacheAwareFilter λŠ” μœ μ €κ°€ 인증된 ν›„ μ €μž₯된 HttpServletRequest λ₯Ό μ–»κΈ° μœ„ν•΄ RequestCache λ₯Ό μ‚¬μš©ν•œλ‹€. λ°˜λ©΄μ— ExceptionTranslationFilter λŠ” μœ μ €λ₯Ό 둜그인 endpoint둜 λ¦¬λ‹€μ΄λ ‰νŠΈ ν•˜κΈ° μ „μ΄λ‚˜ AuthenticationException 을 λ°œκ²¬ν•œ 후에 HttpServletRequest λ₯Ό μ €μž₯ν•˜κΈ° μœ„ν•΄ RequestCache λ₯Ό μ‚¬μš©ν•œλ‹€.

기본적으둜, HttpSessionRequestCache κ°€ μ‚¬μš©λœλ‹€. μ•„λž˜ μ½”λ“œλŠ” RequestCache κ΅¬ν˜„μ²΄λ₯Ό μ»€μŠ€ν…€ν•˜λŠ” 방법에 λŒ€ν•΄ μ„€λͺ…ν•œλ‹€. μ €μž₯된 request에 continue λΌλŠ” μ΄λ¦„μ˜ νŒŒλΌλ―Έν„°κ°€ μ‘΄μž¬ν•˜λŠ”μ§€ μ•„λ‹Œμ§€ HttpSession 을 μ²΄ν¬ν•œλ‹€.

Prevent the Request From Being Saved

μœ μ €μ˜ μΈμ¦λ˜μ§€ μ•Šμ€ requestλ₯Ό μ„Έμ…˜μ— μ €μž₯ν•˜κ³  μ‹Άμ§€ μ•Šμ€ μ—¬λŸ¬κ°€μ§€ μ΄μœ κ°€ μžˆμ„ 것이닀. μ €μž₯ λ‚΄μš©μ„ μœ μ €μ˜ λΈŒλΌμš°μ €λ‘œ μ €μž₯μ‹œν‚€κ³  μ‹Άκ±°λ‚˜ λ°μ΄ν„°λ² μ΄μŠ€μ— μ €μž₯μ‹œν‚€κ³  싢을 수 μžˆλ‹€. λ˜λŠ” 둜그인 ν•˜κΈ° μ „ μœ μ €κ°€ λ°©λ¬Έν•˜λ €ν•œ νŽ˜μ΄μ§€ λŒ€μ‹ μ— 항상 ν™ˆ νŽ˜μ΄μ§€λ‘œ λ¦¬λ‹€μ΄λ ‰μ…˜μ„ μ›ν•˜λ©΄ 이 κΈ°λŠ₯을 차단할 수 μžˆλ‹€.

이λ₯Ό μœ„ν•΄μ„œ NullRequestCache κ΅¬ν˜„μ²΄λ₯Ό μ‚¬μš©ν•  수 μžˆλ‹€.

RequestCacheAwareFilter

RequestCacheAwareFilter λŠ” original requestλ₯Ό replay ν•˜κΈ° μœ„ν•΄ RequestCache λ₯Ό μ‚¬μš©ν•œλ‹€.

Logging

μŠ€ν”„λ§ μ‹œνλ¦¬ν‹°λŠ” κ΄€λ ¨λœ λͺ¨λ“  μ‹œνλ¦¬ν‹° μ΄λ²€νŠΈμ— λŒ€ν•΄ DEBUG와 TRACE 레벨둜 포괄적인 λ‘œκΉ…μ„ μ œκ³΅ν•œλ‹€. μ΄λŠ” λ³΄μ•ˆμ„ μœ„ν•΄ μŠ€ν”„λ§ μ‹œνλ¦¬ν‹°κ°€ requestκ°€ κ±°λΆ€λœ 이유λ₯Ό response body에 λ‹΄μ§€ μ•ŠκΈ° λ•Œλ¬Έμ— μœ μš©ν•  수 μžˆλ‹€. λ§Œμ•½ 401 μ—λŸ¬λ‚˜ 403 μ—λŸ¬λ₯Ό λ§Œλ‚œλ‹€λ©΄, μ–΄λ–»κ²Œ μ§„ν–‰λ˜κ³  μžˆλŠ”μ§€ μ΄ν•΄ν•˜λŠ” 것을 λ•λŠ” 둜그 λ©”μ„Έμ§€λ₯Ό μ°ΎλŠ” 것이 μ’‹λ‹€.

ν•œ μœ μ €κ°€ POST requstλ₯Ό CSRF 토큰 없이 CSRF ν”„λ‘œν…μ…˜μ΄ ν™œμ„±ν™”λœ λ¦¬μ†ŒμŠ€μ— POST requestλ₯Ό μ‹œλ„ν•˜λŠ” 예λ₯Ό λ“€μ–΄λ³΄μž. λ‘œκ·Έμ—†μ΄ μ‚¬μš©μžλŠ” μ™œ requestκ°€ κ±°μ ˆλλŠ”μ§€μ— λŒ€ν•œ μ΄μœ μ— λŒ€ν•œ μ„€λͺ… 없이 403 μ—λŸ¬λ₯Ό λ§ˆμ£Όν•œλ‹€. κ·ΈλŸ¬λ‚˜ λ§Œμ•½ μŠ€ν”„λ§ μ‹œνλ¦¬ν‹°μ— λŒ€ν•΄ λ‘œκΉ…μ„ μ μš©ν•œλ‹€λ©΄, λ‹€μŒκ³Ό 같은 λ©”μ„Έμ§€λ₯Ό λ³Ό 수 μžˆλ‹€.

CSRF 토큰이 μ—†λ‹€λŠ” 것이 λͺ…ν™•ν•΄μ§€κ³  requestκ°€ 거절된 이유λ₯Ό μ•Œκ²Œλœλ‹€.

μ–΄ν”Œλ¦¬μΌ€μ΄μ…˜μ΄ λͺ¨λ“  μ‹œνλ¦¬ν‹° μ΄λ²€νŠΈμ— λŒ€ν•΄ 둜그λ₯Ό 남기도둝 μ„€μ •ν•˜κΈ° μœ„ν•΄, λ‹€μŒ 섀정을 μΆ”κ°€ν•œλ‹€.

application.proerties

logback.xml

Last updated