시큐리티는 앞서 살펴본 인증 객체와 함께 SecurityFilter의 흐름에 따라 인증, 인가를 처리한다. 필자가 느끼기엔 필터들마다 각각의 역할이 명확하게 느껴졌다. 때문에 10개가 넘는 필터들이 서로를 해치치않고 자신의 기능에 충실했다.
지금부터는 각 필터의 역할이 무엇인가에 집중해보면 좋을 것 같다.
비동기 처리인 @Async
를 사용할 때 별도의 Thread에서 처리하게 된다. 하지만 SecurityContext는 Thread-Safe 하기 때문에 별도의 처리가 필요하다.
SecurityContextHolder
SecurityContext가 ThreadLocal에 저장되기 위한 객체이다.
자식 스레드에 공유하게 해주는 전략으로 변경한다.
MODE_THREADLOCAL
-> MODE_INHERITABLETHREADLOCAL
SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
필터 안의 SecurityContextCallableProcessingInterceptor
는 다음과 같은 작업을 한다.
preProcess()
: Callable 실행 전, Async의 Thread로 넘어가기 직전에 SecurityContext를 SecurityContextCallableProcessingInterceptor에 저장한다. postProcess()
: Callable 실행 후, 저장되어 있던 SecurityContext를 제거한다. @Override
public <T> void preProcess(NativeWebRequest request, Callable<T> task) {
this.securityContextHolderStrategy.setContext(this.securityContext);
}
@Override
public <T> void postProcess(NativeWebRequest request, Callable<T> task, Object concurrentResult) {
this.securityContextHolderStrategy.clearContext();
}
SecurityContextPersistenceFilter가 Spring 5.7.2부터 @Deprecated
되었지만, 역할은 거의 비슷하다.
시큐리티는 기본적으로 HttpSessionSecurityContextRepository을 사용하여 인증 정보가 HttpSession에 저장된다. 유저 요청이 오면 SecurityContext를 가져오고 SecurityContextHolder에 세팅한다.
인증 객체를 살펴봤을 때 SecurityContextHolder에 SecurityContext가 저장된다는 것을 알 수 있었다. 저장된 인증 객체를 SecurityContextRespository를 이용하여 정보를 가져온 것이다.
차이점이라면,
HeaderWriterFilter는 요청 전/후에 시큐리티 관련 헤더를 Response에 추가한다.
// 요청 전
private void doHeadersBefore(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws IOException, ServletException {
writeHeaders(request, response);
filterChain.doFilter(request, response);
}
// 요청 후
private void doHeadersAfter(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws IOException, ServletException {
HeaderWriterResponse headerWriterResponse = new HeaderWriterResponse(request, response);
HeaderWriterRequest headerWriterRequest = new HeaderWriterRequest(request, headerWriterResponse);
try {
filterChain.doFilter(headerWriterRequest, headerWriterResponse);
}
finally {
headerWriterResponse.writeHeaders();
}
}
shouldWriteHeadersEagerly 설정에 따라서 response에 Header를 요청 전, 요청 후에 header를 추가할지를 판단한다. 기본값은 False로 요청 후에 추가한다.
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
if (this.shouldWriteHeadersEagerly) { // Default false
doHeadersBefore(request, response, filterChain);
}
else {
doHeadersAfter(request, response, filterChain);
}
}