KKanging

[SpringSecurity] Security 전체적인 아키텍처 본문

백엔드/SpringSecurity

[SpringSecurity] Security 전체적인 아키텍처

천방지축 개발자 2024. 5. 17. 19:29

DelegatingFilterProxy

DelegatingFilterProxy란?

>> Spring Security 의존성을 추가하면 Security에서 제공하는 Security 기능을 지원하는
Filter인 DelegatingFilterProxy를 추가한다.

요청이 들어오면 DelegatingFilterProxy는 어떻게 filter를 실행시킬까?

스프링 Bean을 찾아 요청을 넘겨주는 서블릿 필터이다.
스프링 Bean으로 등록된 Filter를 찾고 실행을 시킨다. -> 이것이 FilterChainProxy

 

DelegatingFilterProxy 코드 살펴보기

public class DelegatingFilterProxy extends GenericFilterBean {

		...
		
		@Nullable
		private volatile Filter delegate;

		public DelegatingFilterProxy(Filter delegate) {
			Assert.notNull(delegate, "Delegate Filter must not be null");
			this.delegate = delegate;
		}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {

		// Lazily initialize the delegate if necessary.
		Filter delegateToUse = this.delegate;
		if (delegateToUse == null) {
			synchronized (this.delegateMonitor) {
				delegateToUse = this.delegate;
				if (delegateToUse == null) {
					WebApplicationContext wac = findWebApplicationContext();
					if (wac == null) {
						throw new IllegalStateException("No WebApplicationContext found: " +
								"no ContextLoaderListener or DispatcherServlet registered?");
					}
					delegateToUse = initDelegate(wac);
				}
				this.delegate = delegateToUse;
			}
		}

		// Let the delegate perform the actual doFilter operation.
		invokeDelegate(delegateToUse, request, response, filterChain);
	}
	protected void invokeDelegate(
			Filter delegate, ServletRequest request, ServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {

		delegate.doFilter(request, response, filterChain);
	}
}
  • 생성자로 delegate라는 필드값을 주입받고
  • delegate.doFilter를 실행한다.

FilterChainProxy

Spring Security의 서블릿 지원은 FilterChainProxy에 포함되어 있다. 

FilterChainProxy는 Spring Security에서 제공하는 특수 필터로, SecurityFilterChain을 통해 
여러 필터 인스턴스에 위임할 수 있다. 

FilterChainProxy는 Bean이기 때문에 일반적으로 DelegatingFilterProxy로 래핑된다.

>>DelegatingFilterProxy 에 의해 호출되어 SecurityFilterChain을 들고있는 Bean이다.

 

SecurityFilterChain

SecurityFilterChain 이란

SecurityFilterChain은 FilterChainProxy가 가지고 있는 Bean으로 등록되어 List 형태로 가지고
있는 chain 이다.

Security 의 핵심 로직이 수행되는 FilterChain이다.

 

SecurityFilterChain이 왜 FilterChainProxy 에 등록될까?

사실 공부하면서 FilterChainProxy에 등록되는게 왜인지 의문점이 들었다.

DelegatingFilterProxy 는 Security에만 있는 구조가 아니다.
Spring 프레임워크와 Servlet의 Filter는 다른 영역이다.
-> 엄밀히 말하면 Filter 안에 Spring 영역이 있는 셈이다.

그래서 Bean으로 등록한 객체를 Filter에 주입 받을 수 없다.
주입 받기 위해 DelegatingFilterProxy라는 계층을 통해 Bean으로 등록된 객체를 주입할 수 있게하는
것이다.

하지만 이는 SpringBoot 가 생겨나면서 필요 없어졌다.
SpringBoot 는 톰켓을 내장하게 되면서 Filter안에 Bean을 주입할 수 있게 되었다.

그래서 DelegatingFilterProxy는 예전 Spring 프레임워크의 코드이다.

 

멀티 SecurityFilterChain

그냥 Security 의존성만 추가한다면 디폴트로 제공하는 SecurityFilterChain 1개만 등록된다.

하지만 여러가지 SecurityFilterChain을 등록 할 수 있다.

 

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain1(HttpSecurity http) throws Exception {

        return http.build();
    }
    
    @Bean
    public SecurityFilterChain filterChain2(HttpSecurity http) throws Exception {

        return http.build();
    }

}
  • 위 설정 처럼 아무것도 설정하지 않고 SecurityFilterChain를 등록만 하면

위와 같이 11개의 필터만 등록 된것을 볼 수 있다.

위와 같은 설정으로 Filter를 제외할 수 있다.

  • 기본 디폴트로 제공하는 Security 필터는 다음과 같다.

멀티 SecurityFilterChain의 주의점

멀티 FilterChain을 등록할때는 등록된 순서대로 적용된다.
따라서 만약 1,2번째 filterChain 모두 해당 URL이 적용되는 FilterChain이라 할지라도
1번의 FilterChain만 적용된다.

또한 두개의 기능을 다르게 할 수도 있다.
  • 경로 매핑 securityMatchers
@Bean
public SecurityFilterChain filterChain1(HttpSecurity http) throws Exception {

    http
            .securityMatchers((auth) -> auth.requestMatchers("/user"));

    http
            .authorizeHttpRequests((auth) -> auth
                    .requestMatchers("/user").permitAll());

    return http.build();
}

@Bean
public SecurityFilterChain filterChain2(HttpSecurity http) throws Exception {

    http
            .securityMatchers((auth) -> auth.requestMatchers("/admin"));

    http
            .authorizeHttpRequests((auth) -> auth
                    .requestMatchers("/admin").authenticated());

    return http.build();
}
  • 특정 요청은 필터를 거치지 않도록
    • 아래 URL 을 받는 0번 필터로 등록되게 된다.
    • 보통 정적 파일 같은 요청은 SecurityFilterChain을 거치지 않기 때문에
@Bean
public WebSecurityCustomizer webSecurityCustomizer() {

    return web -> web.ignoring().requestMatchers("/img/**");
}

SecurityFilterChain에 커스텀 필터 등록

//특정필터이전
http
        .addFilterBefore(추가할필터, 기존필터.class);

//특정필터위치
http
        .addFilterAt(추가할필터, 기존필터.class);

//특정필터이후
http
        .addFilterAfter(추가할필터, 기존필터.class);

ExceptionTranslationFilter

 AccessDeniedException 와 AuthenticationException 를 HTTP 응답으로 변환해준다
 
 -> 이는 다음 ExceptionTranslationFilter 다루면서 자세히 설명

 

참고자료

Architecture :: Spring Security

 

Architecture :: Spring Security

The Security Filters are inserted into the FilterChainProxy with the SecurityFilterChain API. Those filters can be used for a number of different purposes, like authentication, authorization, exploit protection, and more. The filters are executed in a spec

docs.spring.io