SecurityFilterChain은 여러 개 존재할 수 있다. 그리고 경로에 따라 다른 SecurityFilterChain을 거치도록 할 수 있다. 예를 들어 위의 그림처럼 /api/** 경로에 대한 SecurityFilterChain과 나머지 경로(/**) 에 대한 SecurityFilterChain을 다르게 설정하면, 각 경로에 따라 서로 다른 필터 체인을 통과하게 된다.
WAS의 FilterChain에 필터를 등록하는 방법
필터를 스프링 빈으로 등록하는 방법은 여러가지가 있는데 대표적으로 다음 두 방법이 있다.
- Filter 인터페이스를 구현하고, @Component를 붙여서 필터를 등록하는 방법
- @Configuration을 붙인 설정 정보 클래스를 만들고, 내부에 FilterRegistrationBean을 반환하는 메서드를 통해 필터를 등록하는 방법 (참고: https://olsohee.tistory.com/62)
중요한 점은 이렇게 등록한 필터는 Spring Secuity의 SecurityFilterChain이 아닌 WAS의 FilterChain에 등록되는 것이다.
예를 들어 다음과 같이, JWT를 검증하는 JwtFilter를 만들고 @Component를 통해 스프링 빈에 등록했다. 이때 JwtFilter는 SecurityFilterChain이 아닌 WAS의 FilterChain에 등록된다.
@Component
public class JwtFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {...}
}
Spring Security의 SecurityFilterChain에 필터를 등록하는 방법
따라서 SecurityFilterChain에 필터를 등록하려면 위와 같은 방식으로 필터를 등록하면 안되고, 다음과 같이 SecutiryFilterChain 객체를 반환하는 SecurityFilterChain 설정 메소드를 통해 필터를 등록해야 한다.
// @Component 없음
public class JwtFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {...}
}
@Configuration
@RequiredArgsConstructor
public class WebSecurityConfig {
private final JwtUtils jwtUtils;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.csrf(AbstractHttpConfigurer::disable)
// JwtFilter를 SecurityFilterChain에 등록
.addFilterBefore(new JwtFilter(jwtUtils), UsernamePasswordAuthenticationFilter.class);
return http.build();
}
}
WAS의 FilterChain과 Spring Security의 SecurityFilterChain의 순서
SecurityFilterChain으로 등록한 JwtFilter에서 발생하는 예외를 잡기 위해 JwtFilter 앞 단에 JwtExceptionHandler를 등록한다고 가정하자.
필터를 WAS의 FilterChain에 등록
만약 다음과 같이 JwtExceptionHandler를 @Component를 통해 필터로 등록하면, 필터는 WAS의 FilterChain에 등록된다.
@Component
@Slf4j
public class JwtExceptionHandler implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
log.info("JwtExceptionHandler 실행");
chain.doFilter(request, response);
}
}
@Configuration
@RequiredArgsConstructor
public class WebSecurityConfig {
private final JwtUtils jwtUtils;
private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.csrf(AbstractHttpConfigurer::disable)
.addFilterBefore(new JwtFilter(jwtUtils), UsernamePasswordAuthenticationFilter.class)
;
return http.build();
}
}
따라서 SecurityFilterChain에 등록된 JwtFilter가 먼저 호출되고, 그 뒤로 WAS에 등록된 JwtExceptionHandler가 호출된다. 따라서 JwtFilter에서 발생한 예외를 JwtExceptionHandler에서 처리할 수 없다.
즉 Spring Security의 SecurityFilterChain의 필터들이 먼저 실행되고, WAS의 FilterChain의 필터들이 실행된다는 것을 알 수 있다(Spring Security의 SecurityFilterChain ➡️ WAS의 FilterChain).
필터를 Spring Security의 SecurityFilterChain에 등록
이번에는 필터를 @Component를 통해 스프링 빈으로 등록하지 않고, SecurityFilterChain에 등록한다.
@Slf4j
public class JwtExceptionHandler implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
log.info("JwtExceptionHandler 실행");
chain.doFilter(request, response);
}
}
@Configuration
@RequiredArgsConstructor
public class WebSecurityConfig {
private final JwtUtils jwtUtils;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.csrf(AbstractHttpConfigurer::disable)
.addFilterBefore(new JwtFilter(jwtUtils), UsernamePasswordAuthenticationFilter.class)
// JwtExceptionHandler를 SecurityFilterChain에 등록
.addFilterBefore(new JwtExceptionHandler(), JwtFilter.class)
;
return http.build();
}
}
그러면 SecurityFilterChain에 등록한 순서대로 필터가 실행된다. (JwtExceptionHandler ➡️ JwtFilter)
'Backend > Spring Security' 카테고리의 다른 글
OAuth를 통한 인증 (0) | 2024.01.25 |
---|---|
permitAll 설정의 의미 (0) | 2024.01.22 |
Spring Security의 예외 처리 방식 (0) | 2024.01.22 |
Spring Security의 인가(Authorization) (0) | 2024.01.22 |
Spring Security의 인증(Authentication) (0) | 2024.01.21 |