Spring

Spring Security Architecture (Spring MVC 기반)

쇼팽리스트 2024. 8. 25. 23:52

안녕하세요, 이번에는 Spring MVC 기반의 애플리케이션에 동작하는 Spring Securtiy Architecure에 대해 정리를 해보려고 합니다. 본문은 https://docs.spring.io/spring-security/reference/servlet/architecture.html 을 기반으로 작성되었습니다.

📌 Filter는 무엇인가?

Spring Servlet에 대한 Spring Security의 지원은 Servlet Filter에 기반하고 있습니다. 따라서 Spring Security를 이해하기 위해서는 Spring MVC에서 필터가 어떻게 동작하는지 알고 있는 것이 중요합니다.

 

[사진 1] Single HTTP request에 대한 일반적인 layer

클라이언트가 애플리케이션에 요청을 보내면, Container(Servlet Container)는 요청 URI경로를 기준으로 HttpServletRequest를 처리하기 위한 FilterDispatcherServlet을 포함하는 FilterChain을 생성합니다. 하나의 Servlet은 최대 하나의 HttpServletRequestHttpServletResponse를 처리하나, Filter하나 이상이 사용 될 수 있습니다.

 

📌Filter의 역할은 다음과 같습니다.

  • 특정 Filter가 다음에 올 Filter나 Servlet이 호출되는 것을 막을 수 있습니다.

→ 이 말은 즉 Filter가 특정 조건을 만족하지 못했을 때, 요청이 애플리케이션의 나머지 부분까지 전달되지 않도록 차단할 수 있다는 것입니다. 해당 Filter는 응답(HttpServletResponse)을 직접 생성하고, 다음 단계를 진행하지 않도록 합니다.

  • 특정 Filter가 다음에 올 Filter나 Servlet이 사용하게 될 HttpServletRequestHttpServletResponse를 수정할 수 있습니다.

HttpServletRequest를 수정하여 추가 헤더를 넣거나, 요청의 일부를 변경하거나, 특정 속성을 추가하거나 HttpServletResponse를 수정하여 응답이 클라이언트에게 보내지기 전에 쿠키를 추가하거나 헤더를 수정할 수 있으며 이렇게 수정된 요청과 응답은 다음에 오는 Filter나 Servlet이 사용하게 됩니다.

 

FilterChain 예시

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
    // 애플리케이션의 나머지 부분이 호출되기 전에 무언가를 수행
    chain.doFilter(request, response); 
    // 애플리케이션의 나머지 부분을 호출
    
    // 애플리케이션의 나머지 부분이 호출된 후 무언가를 수행
}

 

Filter는 다음에 오는 FIlter와 Servlet에만 영향을 주기 때문에 Filter의 순서는 매우 중요하게 동작합니다.

📌 DelegatingFilterProxy는 무엇인가?

우선 Filter와 Servlet은 Java EE 표준 클래스로, Spring에 종속되어 있는 객체가 아닙니다. Servlet Container는 자체 표준으로 Filter 인스턴스를 등록할 수 있지만, Spring이 정의한 Bean에 대해서는 알지 못합니다.

 

📌DelegatingFilterProxy의 역할은 다음과 같습니다.

Spring은 DelegatingFilterProxy 라는 특별한 필터를 사용하여 Servlet Container에서 관리하는 Filter가 Spring Context내 에서 정의된 Bean에게 작업을 위임하도록 하여 Spring Bean이 필터의 동작을 수행할 수 있게 됩니다.

[사진 2] DelegatingFilterProxy의 사용 예시

DelegatingFilterProxy 은 ApplicationContext에서 정의된 특정 빈에 Filter 작업을 위임합니다. Spring Security에서는 DelegatingFilterProxy 을 사용하여 SecurityFilterChain Bean에 요청을 위임하여 보안 처리를 하게 됩니다.

📌 FilterChainProxy는 무엇인가?

Spring Security의 Servlet support는 FilterChainProxy에 포함되어 있습니다. FilterChainProxy는 Spring Security에 제공되는 특별한 Filter로 Spring Bean이기 때문에 DelegatingFilterProxy로 wrapped 되어 있습니다.

또한 jakarta.servlet.Filter 인터페이스를 구현하여 작성되어 있기 때문에 Servlet Filter으로도 동작합니다.

 

📌FilterChainProxy의 역할은 다음과 같습니다.

FilterChainProxy는 여러 개의 SecurityFilterChain을 관리하고, 각 요청이 적절한 SecurityFilterChain으로 전달되도록 합니다. 즉, FilterChainProxy는 요청을 받아서 적절한 SecurityFilterChain 빈의 doFilter 메서드로 위임합니다.

 

[그림 3] FilterChainProxy의 역할

 

📌 Spring Security의 서블릿 필터 처리 흐름

[그림 4] FilterChainProxy의 동작

 

  1. Client에서 HTTP 요청이 들어오면 Servlet Container에서 요청을 받습니다.
  2. Servlet FilterChain 중 DelegatingFilterProxy Filter가 요청을 받으면 ApplicationContext에서 springSecurityFilterChain 으로 등록된 Bean을 찾습니다. 해당 Bean이 FilterChainProxy입니다.
  3. DelegatingFilterProxy는 해당 Bean을 통해 요청을 Spring SecurityFilterChain으로 전달합니다.
  4. FilterChainProxy가 보안 필터인 SecurityFilterChain을 실행하여 보안 처리를 진행합니다.
  5. 보안 처리가 완료 된 후, Spring MVCDispatcherServlet이 요청을 처리합니다. 

 

📌 SecurityFilterChain는 무엇인가?

SecurityFilterChainFilterChainProxy에 의해 관리되는 Filter로 Spring Security에서 직접적으로 보안 필터 역할을 수행합니다.

또한 Servlet ContainerDelegatingFilterProxy에 등록되는 것이 아닌 FilterChainProxy에 의해 등록 됩니다.

 

[그림 5] SecurityFilterChain의 역할

 

📌SecurityFilterChain 이 FilterChainProxy로 등록되는 것에 대한 이점은 다음과 같습니다.

  • Servlet Container에서는 URL만을 기반으로 호출 되지만 FilterChainProxyRequestMatcher 인터페이스를 사용하여 SecurityFilterChain이 언제 호출하는지 결정하는 데 있어 좀 더 많은 유연성을 제공합니다.
  • FilterChainProxy는 Spring Security의 중심 역할을 하기 때문에 Servlet 문제를 해결할 때 FilterChainProxy에서 Debug 지점을 정하는 것으로 문제를 쉽게 파악할 수 있습니다.

여러 개의 SecurityFilterChain을 사용하는 경우는 다음과 같습니다.

 

[그림 6] 다중 SecurityFilterChain의 사용

위 그림에서 FilterChainProxy는 어떤 SecurityFilterChain이 사용되어야 하는지를 결정합니다. 이때 매칭되는 첫 번째 SecurityFilterChain만 호출됩니다.

예를 들어, URL이 /api/messages/인 경우에는 우선 /api/** 패턴에 매칭되는 SecurityFilterChain0이 호출됩니다.

이 경우 SecurityFilterChainn에도 매칭되지만, SecurityFilterChain0만 호출됩니다.

반면에 URL이 /messages/인 경우, /api/** 패턴에 매칭되지 않으므로 FilterChainProxy는 계속해서 각 SecurityFilterChain을 시도하게 됩니다. 만약 다른 SecurityFilterChain 인스턴스가 매칭되지 않는다면, /** 패턴에 매칭 되는 SecurityFilterChainn이 호출됩니다.

각 SecurityFilterChain은 고유할 수 있으며, 개별적으로 구성될 수 있습니다.