Backend/Spring Security

SecurityFilterChain에 필터를 등록할 때 주의할 점(WAS의 FilterChain과 Spring Security의 SecurityFilterChain의 차이)

olsohee 2024. 1. 22. 16:36

SecurityFilterChain은 여러 개 존재할 수 있다. 그리고 경로에 따라 다른 SecurityFilterChain을 거치도록 할 수 있다. 예를 들어 위의 그림처럼 /api/** 경로에 대한 SecurityFilterChain과 나머지 경로(/**) 에 대한 SecurityFilterChain을 다르게 설정하면, 각 경로에 따라 서로 다른 필터 체인을 통과하게 된다.

WAS의 FilterChain에 필터를 등록하는 방법

필터를 스프링 빈으로 등록하는 방법은 여러가지가 있는데 대표적으로 다음 두 방법이 있다.

  1. Filter 인터페이스를 구현하고, @Component를 붙여서 필터를 등록하는 방법
  2. @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