DelegatingFilterProxy & FilterChainProxy

8015 단어 SpringSpring

ServletFilter

  • WAS에서 톰캣이 올라가서 WAS에서 실행되는 필터이다.
    요청을 받아서 Servlet에 전달하고, Servlet에서 응답을 받아서 클라언트에 전달하는 역할을 수행한다.

  • Servlet Container에서 생성되고 실행이 된다.

  • Servlet Filter는 스프링에서 정의된 빈을 주입해서 사용할 수 없다.
    -> 서로 실행되는 컨테이너가 다르기 때문
    인증/인가 처리는 스프링 컨테이너에 생성된 Filter를 통해서 처리된다.
    실질적인 서블릿 필터가 DelegatingFilterProxy이다.

// web.xml

<filter>
	<filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
	<filter-name>springSecurityChain</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

DelegatingFilterProxy

  • 서블릿 컨테이너와 스프링 컨테이너(어플리케이션 컨텍스트) 사이의 링크를 제공하는 ServletFilter이다.

  • 특정한 이름을 가진 스프링 빈을 찾아 그 빈에게 요청을 위임한다.
    -> 서블릿 필터(서블릿 컨테이너)와 시큐리티 필터(스프링 컨테이너)는 서로 다른 컨테이너에서 생성되고 동작한다.

  • 요청 순서
    - Servlet Filter가 요청을 DelegatingFilterProxy로 전달한다.

    • DelegatingFilterProxy는 해당 요청을 스프링 컨테이너에 생성 된 Filter를 구현한 스프링 빈에 위임한다.
    • springSecurityFilterChain 이름으로 생성된 빈을 ApplicationContext에서 찾아 위임을 요청(실제 보안 처리를 하지 않음)
  1. SecurityFilterAutoConfiguration 클래스에서 DelegatingFilterProxyRegistrationBean를 빈으로 등록한다.
// SecurityFilterAutoConfiguration

@Bean
@ConditionalOnBean(name = DEFAULT_FILTER_NAME)
public DelegatingFilterProxyRegistrationBean securityFilterChainRegistration(SecurityProperties securityProperties){
	DelegatingFilterProxyRegistrationBean registration = new DelegatingFilterProxyRegistrationBean(DEFAULT_FILTER_NAME);
    registration.setOrder(securityProperties.getFilter().getOrder());
    registration.setDispatcherTypes(getDispatcherTypes(securityProperties));
    
    return registration;
}
  1. DelegatingFilterProxyRegistrationBean 클래스 DelegatingFilterProxy 객체를 생성한다.
// DelegatingFilterProxyRegistrationBean

@Override
public DelegatingFilterProxy getFilter(){
	return new DelegatingFilterProxy(this.targetBeanName, getWebApplicationContext()){
    	@Override
        protected void initFilterBean() throws ServletException{
        	// Don't initialize filter bean on init()
        }
    };
}
  1. this.setTargetBeanName(targetBeanName) DelegatingFilterProxy로 요청이 들어올 때 처리를 위임 할 스프링 빈 이름(springSecurityFilterChain)을 설정한다.
// DelegatingFilterProxy

public DelegatingFilterProxy(String targetBeanName, @Nullable WebApplicationContext wac){
	Assert.hasText(targetBeanName, "Target Filter bean name must not be null or empty");
    this.setTargetBeanName(targetBeanName);
    this.webApplicationContext = wac;
    if (wac != null){
    	this.setEnvironment(wac.getEnvironment());
    }
}
  1. 요청이 들어오면 가장 먼저 DelegatingFilterProxy의 doFilter() 함수가 호출된다. doFilter() 함수는 요청을 위임할 필터(springSecurityFilterChain)을 찾아서 해당 요청을 위임(invokeDelete())한다.
// DelegatingFilterProxy

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletExcpetion, IOException{
	
    // Lazily nitialize 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);
    
}
  1. invokeDelete() 함수 호출 시 내부에서 FilterChainProxy의 doFilter() 함수를 호출하고, doFilterInternal() 함수에서는 등록 된 Filter 목록을 가지고 인증/인가 처리를 진행한다.
// FilterChainProxy

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException{
	boolean clearContext = request.getAttribute(FILTER_APPLIED) == null;
    if(clearContext){
    	try{
        	request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
            doFilterInternal(request, response, chain);
        }
        finally{
        	SecurityContextHolder.clearContext();
            request.removeAttribute(FILTER_APPLIED);
        }
    }else{
    	doFilterInternal(request, response, chain);
    }
}

FilterChainProxy

  • springSecurityFilterChain의 이름으로 생성되는 필터 빈이다.
  • DelegatingFilterProxy로부터 요청을 위임 받고 실체로 보안을 처리한다.
  • 스프링 시큐리티 초기화 시 생성되는 필터들을 관리하고 제어한다.
  • 스프링 시큐리티가 기본적으로 생성하는 필터
    - WebAsyncManager
    - SecurityContextPersistenceFilter
    - HeaderWriterFilter
    - LogoutFilter
    - AnonymousAutehnticationFilter
    - SessionManagementFilter
    -> 사용자의 요청을 필터 순서대로 호출하여 전달한다.
    사용자 정의 필터를 생성해서 기존의 필터 전후로 추가 가능하다.
    마지막 필터까지 인증 및 인가 예외가 발생하지 않으면 보안을 통과한다.
/**
	Creates the Spring Security Filter Chain
    @return the {@link Filter} that represents the security filter chain
    @throws Exception
*/
@Bean(name = AbstractSecurityWebApplicationInitailizer.DEFAULT_fILTER_NAME)
public Filter springSecurityFilterChain() throws Exception{
	boolean hasConfigurers = webSecurityConfigurers != null && !webSecurityConfigurers.isEmpty();
    
    if(!hasConfigurers){
    	WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor.postProcess(new WebSecurityConfigurerAdapter(){});
        webSecurity.apply(adapter);
    }
    return webSecurity.build();
}
  • webSecurity.build() 함수 호출 시 springSecurityFilterChain이름의 FilterChainProxy 빈을 생성한다.

좋은 웹페이지 즐겨찾기