1. WebAsyncManagerIntegrationFilter.
- WebAsyncManagerIntegrationFilter
- Spring Security에서 중요한 역할을 하는 필터로, SecurityContext와 Spring Web의 WebAsyncManager를 통합함.
- 서블릿에서 비동기 요청 처리(Async Processing)할 때 서블릿 입출력 쓰레드와 작업 쓰레드가 동일한 SecurityContextHolder의 SecurityContext를 참조할 수 있도록 도와줌.
- 즉, SecurityContextHolder의 ThreadLocal 방식에 따라 동일한 쓰레드일 경우에만 SecurityContext에 접근할 수 있는데 비동기 방식의 경우 하나의 작업을 2개의 쓰레드로 수행하기 때문에 해당 부분을 보완해주기 위해서 필터가 존재함.
- 비동기 요청 처리 시 SecurityContext의 일관성을 유지시켜줌.
- SecurityContextCallableProcessingInterceptor를 사용하여 Callable에 SecurityContext를 설정함.
- WebAsyncMagagerIntegrationFilter는 필터 단계에 존재하는데 어떻게 필터를 통과하고 컨트롤러 단계에서 발생하는 쓰레드 이동 문제를 처리할 수 있을까?
- WebAsyncManagerIntegrationFilter가 수행하는 작업과 Callable의 동작 방식에 관련이 있음.
package org.springframework.security.web.context.request.async;
import java.io.IOException;
import java.util.concurrent.Callable;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.context.SecurityContextHolderStrategy;
import org.springframework.util.Assert;
import org.springframework.web.context.request.async.WebAsyncManager;
import org.springframework.web.context.request.async.WebAsyncUtils;
import org.springframework.web.filter.OncePerRequestFilter;
public final class WebAsyncManagerIntegrationFilter extends OncePerRequestFilter {
private static final Object CALLABLE_INTERCEPTOR_KEY = new Object();
private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
.getContextHolderStrategy();
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
SecurityContextCallableProcessingInterceptor securityProcessingInterceptor = (SecurityContextCallableProcessingInterceptor) asyncManager
.getCallableInterceptor(CALLABLE_INTERCEPTOR_KEY);
if (securityProcessingInterceptor == null) {
SecurityContextCallableProcessingInterceptor interceptor = new SecurityContextCallableProcessingInterceptor();
interceptor.setSecurityContextHolderStrategy(this.securityContextHolderStrategy);
asyncManager.registerCallableInterceptor(CALLABLE_INTERCEPTOR_KEY, interceptor);
}
filterChain.doFilter(request, response);
}
public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {
Assert.notNull(securityContextHolderStrategy, "securityContextHolderStrategy cannot be null");
this.securityContextHolderStrategy = securityContextHolderStrategy;
}
}
asyncManager.registerCallableInterceptor(CALLABLE_INTERCEPTOR_KEY, interceptor);
- doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) 메서드.
- 현재 요청의 WebAsyncManager를 가져옴.
- 현재 요청에서 SecurityContextCallableProcessingInterceptor를 확인하고 이미 존재하면 그대로 사용.
- 없으면 새로 생성하여 WebAsyncManager에 등록.
- 필터 체인을 계속 실행하여 요청을 계속 처리함.

- 클라이언트쪽에서 보낸 요청이 필터를 모두 거친 후 스프링 컨테이너에 있는 DispatcherServlet이 해당 요청에 알맞는 Controller 메서드를 찾아 요청하고 Callable을 반환 받음.
- DispatcherServlet이 Callable을 비동기 처리하도록 WebAsyncManager에 요청함.
- WebAsyncManager는 Callable을 특정 TaskExecutor에 할당하여 비동기를 처리함.
- Callable의 작업이 완료되면 WebAsyncManager로 결과값을 리턴해줌.
- WebAsyncManager는 DispatcherServlet에게 Dispatch 요청을 함.
- DispatcherServlet은 WebAsyncManager에게서 결과값을 받은 다음 적절하게 처리하여 응답을 반환함.
- WebAsyncManager는 WebAsyncManagerIntegrationFilter를 통해 기존 쓰레드가 참조하던 SecurityContext를 전달받고 SecurityContextCallableProcessingInterceptor를 활용하여 새로운 쓰레드에서도 기존 SecurityContext를 사용할 수 있음.
- 따라서 쓰레드가 변경되더라도 SecurityContext에서 동일한 값을 획득할 수 있음.