[Spring] Spring Security 내부 동작 과정
#목차
Spring Security 내부 동작 과정
- Spring Security 내부 동작 과정을 알기 위해선 Spring MVC의 기본 동작 원리를 알고 있어야 한다.
- 우선, 아래 Spring MVC 내부 동작 과정에 대해 간략하게 알아보자.
Spring MVC 요청 과정
- 사용자는 웹 브라우저를 통해 특정
URL
로 요청을 보낸다. 그 요청에는 HTTP 메서드, 헤더 정보, 본문 데이터 등이 포함될 수 있다. - 모든 요청은 Spring의 웹 서버에 도착하고,
FrontController
역할을 하는DispatcherServlet
에 의해 처리가 되는데, 요청 URL, HTTP 메서드, 요청 헤더 정보 등을 분석하여 요청을 처리할 수 있는 핸들러를HandlerMapping
에 조회한다. HandlerAdapter
목록에서 요청에 맞는 핸들러를 조회한다.DispatcherServlet
은 조회한 핸들러의 타입을 기반으로HandlerAdapter
를 통해서 핸들러를 실행한다.- 핸들러는 일반적으로
@Controller
나@RestController
와 같은 애노테이션으로 정의되며,@RequestMapping
,@GetMapping
,@PostMapping
등의 애노테이션을 통해 비즈니스 로직을 거쳐Model
객체를 생성하여DispatcherServlet
에 반환한다. DispatcherServlet
은ViewResolver
에게Model
객체를 전달하고, 뷰 이름을 실제 뷰 페이지 경로로 변환하고, View Template Engine(Thymeleaf, JSP 등) 을 이용하여Model
객체의 데이터를 HTML로 렌더링 하는View
객체를 생성한다.- 그렇게 렌더링 되어 생성된
HTML
코드를,DispatcherServlet
에 응답해 준다. DispatcherServlet
은ViewResolver
에서 렌더링 된 HTML 코드를 사용자(Client
)에게HTTP 응답
으로 전송한다.
위 내용이 기본적인 Spring MVC의 요청 흐름이다.
그렇다면, 요청에 문제가 있거나, 권한이 없는 요청 등이 중요한 데이터에 접근하려 한다면 어떻게 처리해야 할까? 이 문제를 Srping Security가 해결할 수 있게 되었다.
Spring Securit 의 기본 동작 원리와 흐름
위 그림은 Spring MVC
에서 Spring Security
를 적용했을 때의 그림이다.
- 사용자는 웹 브라우저 또는 기타 애플리케이션을 통해 요청을 보낸다. 요청은 웹 서버(예:
Nginx
,Apache
)에 도달하고, 웹 서버는 요청을Servlet Container
(예:Tomcat
)로 전달한다. Servlet Container
는 전달 받은 요청을FilterChain
(FilterChain
은Servlet Container
에 등록된 필터들의 목록)에 전달하고, 필터들은 순서대로 실행되며, 요청을 처리하고 다음 필터로 전달할지 여부를 결정한다.FilterChain
의DelegatingFilterProxy
는Spring Context
에서 SecurityFilterChain의@Bean
을 조회한다.Spring Security less than 5.7 version
@Configuration public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeHttpRequests((authz) -> authz .anyRequest().authenticated() ) .httpBasic(withDefaults()); } }
Spring Security 5.7 version or more
@Configuration public class SecurityConfiguration { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { // 각종 설정 들을 구성한다. http .authorizeHttpRequests((authz) -> authz .anyRequest().authenticated() ) .httpBasic(withDefaults()); return http.build(); } }
참고
Spring Security 5.4
에서는SecurityFilterChain
빈(@Bean)을 생성하여HttpSecurity
를 구성하는 기능을 도입하였고,Spring Security 5.7.0-M2
부터WebSecurityConfigurerAdapter
의 기능이 Deprecated 되었다.- 여기서 문제가 발생한다. 필터는
WAS
내에서Spring Security
는Spring Context
에서 각각 독립적인 구성요소로 운영 된다. 즉, 필터에서는 직접적으로 스프링 기능을 활용하기 어렵다. - 그렇다면, 어떻게 Spring Security를 사용할 수 있게 해결했을까? 바로, DelegatingFilterProxy가
Spring Security
의FilterChainProxy
에 위임(delegation)하는 전략을 취하게 되었다. 즉, 위임(delegating)을 통해 문제를 해결하였다.
- 여기서 문제가 발생한다. 필터는
- 그렇게 조회한
@Bean
의SecurityFilterChain
에 설정된 보안(CSRF
,XSS
등), 인증(Authentication
), 인가(Authorization
) 등의 작업을 통해 원하는 접근 제어를 할 수 있다. SecurityFilterChain
에서 설정한 설정 값들이 모두 통과하게 되면 다음 필터의 단계로 넘어가게 되며, 모든FilterChain
의 필터들이 정상적으로 통과하게 되면 클라이언트의 요청은DispatcherServlet
으로 넘어가게 된다.
참고
Spring Security
는 Servlet Filter 기반으로 동작하며, Servlet과 Srping Context는 다르다.
-Servlet Filter
: 웹의 모든 요청을 가로채어 먼저 처리하는 역할을 수행한다, 톰캣과 같은 WAS에서 작동한다.
-Spring Context
: 스프링 IoC 컨테이너를 기반으로 구축되며,DI
,AOP
등 다양한 기능을 제공한다.
package org.springframework.security.web
public interface SecurityFilterChain {
boolean matches(HttpServletRequest request);
List<Filter> getFilters();
}
SecurityFilterChain
의 구현체는matches()
를 통해 자신에게 온 요청인지 확인하고 매칭된다면getFilters()
를 통해FilterChainProxy
에게 필터 체인을 제공한다. 즉, 여러 개의 필터 체인을 가지고 있을 수 있으며 요청에 따라 다른 보안 로직을 처리할 수 있다는 뜻이다.
그렇다면 왜 서블릿 필터와 스프링 컨텍스트로 나누는 전략을 취했을까?
- 분리된 책임 : 서블릿 필터(Servlet Filter) 는 주로 웹 요청과 응답에 대한 사전처리 및 사후 처리를 담당한다. 반면, 스프링 컨텍스트(Spring Context) 는 애플리케이션의 비즈니스 로직, 빈 관리, 데이터 접근, 서비스 계층 등을 관리하게 된다. 이러한 분리는 관심사의 분리(Separation of concerns, SoC) 원칙을 따르며, 각각의 영역이 자신의 역할에 집중할 수 있도록 설계되었다. 그래서 개발자는 각 영역에 맞는 코드 작업과 테스트할 수 있다.
- 유연성과 확장성 : 서블릿 필터(Servlet Filter) 와 스프링 컨텍스트(Spring Context) 를 분리함으로써, 각각 독립적으로 발전하고 확장할 수 있는 유연성을 제공한다. 예를 들어, 보안, 로깅, 인증 같은 기능을 필터에서 처리하고, 비즈니스 로직은 스프링 컨텍스트에서 처리할 수 있다. 이는 코드의 재사용 성과 유지보수 성을 향상시키게 된다.
- 기술적인 호환성 : 서블릿 API는 JavaEE 사양의 일부로, 다양한 웹 애플리케이션 서버(WAS)에서 지원된다. 스프링 프레임워크는 이러한 서블릿 API 위에서 동작하며, 스프링 기능을 제공한다. 이 구조는 스프링 기반 애플리케이션을 다양한 환경에서 실행할 수 있는 기술적 호환성을 보장한다.
- 보안의 강화 : 보안 관련 처리를 서블릿 필터(Servlet Filter) 단계에서 먼저 처리함으로써, 악의적인 요청이 스프링 컨텍스트(Spring Context) 내부의 비즈니스 로직에 도달하기 전에 차단될 수 있다. 이는 애플리케이션의 전반적인 보안 수준을 향상시키게 된다.
이러한 이유로, 서블릿 필터(Servlet Filter) 와 스프링 컨텍스트(Spring Context) 를 분리하여 애플리케이션의 구조를 더욱 견고하고 안전하게 만들며, 개발자가 유연하고 효율적인 애플리케이션을 개발하고 관리할 수 있도록 돕는다.
물론, 이러한 장점만 있는 것은 아니다.
가장 큰 단점은 복잡성이 증가하여 두 시스템을 별도로 관리해야 하기 때문에 개발 및 유지 관리에 더 많은 노력과 시간이 투자가 된다.
하지만, 앞서 언급한 장점들을 고려했을 때, 일반적으로 웹 애플리케이션 개발에서는 서블릿 필터(Servlet Filter) 와 스프링 컨텍스트(Spring Context) 를 나누는 전략을 사용하는 것이 유리하며, 대규모 서비스나 복잡한 웹 애플리케이션 개발 시에는 이러한 전략의 장점들이 뚜렷이 나타나게 된다.