
이번에는 DefaultSecurityFilterChain에 기본적으로 등록되는 필터로 각각 12, 13, 14, 15번 째에 위치하는 RequestCacheAwareFilter, SecurityContextHolderAwareRequestFilter, AnonymousAuthenticationFilter, ExceptionTranslationFilter에 대해 알아보자.
이 필터는 HTTP 요청에서 처리할 작업이 있고 현재 요청에서 그 작업을 수행하기 위해 등록된다.
커스텀 SecurityFilterChain에도 기본적으로 등록된다.
-> 핸들 시점(2번)에서 일어나는 일
예외를 처리하는 필터인
ExceptionTranslationFilter에서ExceptionTranslationFilter이후에 발생하는 예외들을 모두 받는다.
AccessDeniedException과 같은 몇몇 예외 발생 시, ExceptionTranslationFilter의 내부 메소드 sendStartAuthentication() 메소드가 실행되는데 여기서 requestCache를 저장한다.
protected void sendStartAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
AuthenticationException reason) throws ServletException, IOException {
SecurityContext context = this.securityContextHolderStrategy.createEmptyContext();
this.securityContextHolderStrategy.setContext(context);
this.requestCache.saveRequest(request, response);
this.authenticationEntryPoint.commence(request, response, reason);
}
이후에 저장된 requestCache를 확인 후 현재 요청에 적용 시킨다.
public class RequestCacheAwareFilter extends GenericFilterBean {
private RequestCache requestCache;
public RequestCacheAwareFilter() {
this(new HttpSessionRequestCache());
}
public RequestCacheAwareFilter(RequestCache requestCache) {
Assert.notNull(requestCache, "requestCache cannot be null");
this.requestCache = requestCache;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// request 캐시에서 이전 요청에 캐싱해둔 데이터가 있는지 조회
HttpServletRequest wrappedSavedRequest = this.requestCache.getMatchingRequest((HttpServletRequest) request,
(HttpServletResponse) response);
// 있다면 다음 필터로 넘길때 현재 요청에 덮어 쓰기
chain.doFilter((wrappedSavedRequest != null) ? wrappedSavedRequest : request, response);
}
}
ServletRequest 요청에 스프링 시큐리티 API를 다룰 수 있는 메소드를 추가한다.
커스텀 SecurityFilterChain에도 기본적으로 등록된다.
authenticate() : 사용자가 인증 여부를 확인하는 메소드login() : 사용자가 AuthenticationManager를 활용하여 인증을 진행하는 메소드logout() : 사용자가 로그아웃 핸들러를 호출할 수 있는 메소드AsyncContext.start() : Callable를 사용하여 비동기 처리를 진행할 때 SecurityContext를 복사하도록 설정하는 메소드여러 필터를 거치면서 현재 지점까지 SecurityContext 값이 null인 경우 Anonymous 값을 넣어주기 위해 사용된다.
커스텀 SecurityFilterChain에도 기본적으로 등록된다.
이 필터 이후에 발생하는 인증, 인가 예외를 핸들링하기 위해 사용된다.
커스텀 SecurityFilterChain에도 기본적으로 등록된다.
public class ExceptionTranslationFilter extends GenericFilterBean implements MessageSourceAware {
...
private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 다음 필터를 실행하는 부분에 예외 처리 구문이 존재
try {
chain.doFilter(request, response);
}
catch (IOException ex) {
throw ex;
}
catch (Exception ex) {
// Try to extract a SpringSecurityException from the stacktrace
Throwable[] causeChain = this.throwableAnalyzer.determineCauseChain(ex);
RuntimeException securityException = (AuthenticationException) this.throwableAnalyzer
.getFirstThrowableOfType(AuthenticationException.class, causeChain);
if (securityException == null) {
securityException = (AccessDeniedException) this.throwableAnalyzer
.getFirstThrowableOfType(AccessDeniedException.class, causeChain);
}
if (securityException == null) {
rethrow(ex);
}
if (response.isCommitted()) {
throw new ServletException("Unable to handle the Spring Security Exception "
+ "because the response is already committed.", ex);
}
// 예외인 경우 handleSpringSecurityException()를 호출해 처리함
handleSpringSecurityException(request, response, chain, securityException);
}
}
...
}
handleSpringSecurityException 함수에서는 인증 / 인가 예외로 나누어 처리한다.
private void handleSpringSecurityException(HttpServletRequest request, HttpServletResponse response,
FilterChain chain, RuntimeException exception) throws IOException, ServletException {
if (exception instanceof AuthenticationException) {
handleAuthenticationException(request, response, chain, (AuthenticationException) exception);
}
else if (exception instanceof AccessDeniedException) {
handleAccessDeniedException(request, response, chain, (AccessDeniedException) exception);
}
}
ExceptionTranslationFilter 필터 이전에 발생하는 예외의 경우 처리하지 못한다. 예외 처리는 호출한 필터가 진행할 수 있다.
예를 들어, UsernamePasswordAuthenticationFilter는 ExceptionTranslationFilter 보다 앞에 존재하기 때문에 이 경우에는 예외를 처리할 수 없다.