[Spring] Spring Security 내부 동작 과정
![[Spring] Spring Security 내부 동작 과정](/assets/img/development/server/2023-09-19_spring_security_overview/spring_security_cover.png)
#목차
Spring Security 내부 동작 과정
Spring Security 내부 동작 과정을 요약한 이미지
- Spring Security 내부 동작 과정을 알기 위해선 Spring MVC의 기본 동작 원리를 알고 있어야 한다.
- 우선, 아래 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 Security 내부 동작 과정을 요약한 이미지
위 그림은 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) 를 나누는 전략을 사용하는 것이 유리하며, 대규모 서비스나 복잡한 웹 애플리케이션 개발 시에는 이러한 전략의 장점들이 뚜렷이 나타나게 된다.