Filter
필터는 요청이나 응답 또는 둘 다에 대해 필터링 작업을 수행하는 개체
필터는 doFilter 메소드에서 필터링을 수행한다
Filter는 doFilter를 구현해야함
public interface Filter {
public default void init(FilterConfig filterConfig) throws ServletException {}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException;
public default void destroy() {}
}
FilterChainProxy Class
private void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
FirewalledRequest firewallRequest = this.firewall.getFirewalledRequest((HttpServletRequest) request);
HttpServletResponse firewallResponse = this.firewall.getFirewalledResponse((HttpServletResponse) response);
List<Filter> filters = getFilters(firewallRequest);
if (filters == null || filters.size() == 0) {
if (logger.isTraceEnabled()) {
logger.trace(LogMessage.of(() -> "No security for " + requestLine(firewallRequest)));
}
firewallRequest.reset();
chain.doFilter(firewallRequest, firewallResponse);
return;
}
if (logger.isDebugEnabled()) {
logger.debug(LogMessage.of(() -> "Securing " + requestLine(firewallRequest)));
}
VirtualFilterChain virtualFilterChain = new VirtualFilterChain(firewallRequest, chain, filters);
virtualFilterChain.doFilter(firewallRequest, firewallResponse);
}
getFilters(firewallRequest); 를 디버깅해보자!
Filter의 순서를 알 수 있다.
Filter의 순서
순서를 보려면 FilterOrderRegistration 를 봐야한다.
Filter들은 100번 부터 시작해서 100씩 증가된다. 즉, 100이 가장먼저 200, 300, 400 이런 순서대로 필터가 적용됩니다.
굳이 이렇게 만든 이유는 100이라는 공백 사이사이에 커스텀 필터를 넣을 수 있도록 한 것입니다.
private static final int INITIAL_ORDER = 100;
private static final int ORDER_STEP = 100;
private final Map<String, Integer> filterToOrder = new HashMap<>();
FilterOrderRegistration() {
Step order = new Step(INITIAL_ORDER, ORDER_STEP); put(ChannelProcessingFilter.class, order.next());
order.next();
put(WebAsyncManagerIntegrationFilter.class, order.next());
put(SecurityContextPersistenceFilter.class, order.next());
}
Spring Security의 동작은 Filter로 동작한다.
필터에 동작하는 순서를 정해줘서 원하는대로 유기적으로 동작할 수 있다.
많이 쓰이는 FIlter는 아래와 같다.
SecurityContextPersistenceFilter
BasicAuthenticationFilter
UsernamePasswordAuthenticationFilter
CsrfFilter
RememberMeAuthenticationFilter
AnonymousAuthenticationFilter
FilterSecurityInterceptor
ExceptionTranslationFilter
SecurityContextPersistenceFilter를 보면 아래처럼 GenericFilterBean를 상속하고 있고 GenericFilterBean는 Filter를 상속한다.
‼️ SecurityContextPersistenceFilter는 Filter를 구현한다.
public class SecurityContextPersistenceFilter extends GenericFilterBean {
public abstract class GenericFilterBean implements Filter
SpringSecurity에는 다양한 Filter들이 존재하는데 그중에 하나가 SecurityContextPersistenceFilter이다.
SecurityContextPersistenceFilter
- SecurityContextPersistenceFilter는 보통 두번째로 실행되는 필터 아키텍처
- SecurityContextPersistenceFilter는 SecurityContext를 찾아와서 SecurityContextHolder에 넣어주는 역할을 하는 Filter
- SecurityContext를 찾았는데 없다면 그냥 새로 하나 생성해준다.
첫번째로 실행되는 필터는 Async 요청에 대해서도 SecurityContext를 처리할수 있도록 해주는 WebAsyncManagerIntegrationFilter
public class SecurityContextPersistenceFilter extends GenericFilterBean {
private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {
SecurityContext contextBeforeChainExecution = this.repo.loadContext(holder);
try {
SecurityContextHolder.setContext(contextBeforeChainExecution);
chain.doFilter(holder.getRequest(), holder.getResponse());
}
finally {
SecurityContext contextAfterChainExecution = SecurityContextHolder.getContext();
// Crucial removal of SecurityContextHolder contents before anything else.
SecurityContextHolder.clearContext();
this.repo.saveContext(contextAfterChainExecution, holder.getRequest(), holder.getResponse());
}
}
}
HttpSession
SecurityContextPersistenceFilter는 SecurityContext가 있으면 그걸 사용하고 없다면 새로 생성한다.
SecurityContext가 있는지 없는지 확인을 HttpSession에서 가져온다.