본문 바로가기
개발/SpringBoot

(Java/Kotlin)[Spring Security] - Custom Filter를 등록하는 방법 (feat.OncePerRequestFilter)

by Mingvel 2023. 11. 25.

Spring Filter

Spring을 사용하는 환경에서 Custom Filter를 만들어 사용하는 일반적인 목적은 두 가지가 있습니다

 

(1) Client로부터 요청이 들어올 때, 요청이 Controller에 유입되기 전 사전처리 혹은 후처리를 하고 싶은 경우

(2) 인증, 인가와 같은 내용들로 하여금 Controller 유입 전에 트래픽 자체를 Filtering 하고 싶은 경우

 

(1) 번의 대표적인 예시로는 request, response 데이터에 대해 로깅하고 싶은 내용들을 Spring WebContext 유입시점과 나가는 시점에 수행하는 것이 있습니다

(2) 번의 대표적인 예시로는 UserDetails + JWT를 이용해 회원가입/로그인 기능을 구현할 수 있습니다

 

위에서 언급한 예시들은 모두 Service Layer에서도 수행이 가능합니다

 

그렇다면 왜 위 기능들을 Service Layer에서 수행하지 않고, Filter라는 개념을 이용하는 것일까요?

 

위 질문에 대한 다양한 견해가 있겠지만

 

제가 생각하는 가장 큰 이유 두 가지는 다음과 같습니다

 

1. 전처리, 후처리에 대한 작업을 비즈니스 로직과 분리 (관심사의 분리)

2. 유효하지 않은 요청 사전 차단 및 빠른 응답

 

Filter를 사용할 땐, 일반적으로 OncePerRequestFilter를 구현한 구현체로 클래스를 정의해서 사용합니다

 

OncePerRequestFilter는 Single Thread 단위로 동작하는 Filter로, Single Thread 단위로 동작하는 서비스에서 주로 사용합니다

 

OncePerRequestFilter를 구현한 구현체의 예시는 다음과 같습니다

class ApiKeyFilter(
    // some Service
    // some Repository
) : OncePerRequestFilter() {

    override fun doFilterInternal(
        request: HttpServletRequest,
        response: HttpServletResponse,
        filterChain: FilterChain
    ) {
        // Service Call
        // Repository Call
        logger.info("test log")
        filterChain.doFilter(request, response)
    }
}

 

필수적으로 구현해야 하는 함수는 doFilterInternal 함수로, HttpServletRequest와 Response, 그리고 FilterChain을 인자로 받아

 

해당 Filter의 관심사에 대한 부분을 모두 처리한 후, FilterChain의 doFilter 함수를 호출해 줌으로써, 요청을 다음 Filter에게 넘기는 작업을 수행합니다. (잊으면 안 됩니다)

 

FilterChain은 책임연쇄 패턴의 구현체로 관심 있으신 분은 링크 속 내용을 확인해 보세요

 

다음으로는 이 글의 주제이기도 한 Filter를 등록하는 방법에 대해서 다뤄보겠습니다

 

 

(1) @Component 어노테이션을 명시해 Spring Container에서 Bean으로 관리하는 방법

@Component
class ApiKeyFilter(
) : OncePerRequestFilter() {

    override fun doFilterInternal(
        request: HttpServletRequest,
        response: HttpServletResponse,
        filterChain: FilterChain
    ) {
        filterChain.doFilter(request, response)
    }
}

 

@Component 또는 @Bean 어노테이션을 사용하여 Filter를 빈으로 등록하면

 

Spring Framework의 일부가 되어 의존성 주입, 라이프사이클 관리 등 Spring이 제공하는 모든 기능을 활용할 수 있습니다.

 

이는 필터가 Spring의 ApplicationContext에 의해 관리되고 초기화되며

 

Spring Boot의 AutoConfiguration 메커니즘에 의해 서블릿 컨테이너에 등록됩니다.

 

 

(2) FilterChain Bean에 직접 Filter를 등록하는 방법

@Configuration
@EnableWebSecurity
class SecurityConfig {

    @Bean
    fun filterChain(http: HttpSecurity, apiKeyFilter: ApiKeyFilter): SecurityFilterChain {
        http.addFilterBefore(apiKeyFilter, AuthorizationFilter::class.java)

        return http.build()
    }
}

이 방법은 Filter를 Spring Security의 FilterChain에 명시적으로 등록하는 방법입니다

 

이는 HttpSecurity를 사용하여 구성되며, 주로 WebSecurityConfigurerAdapter를 상속받는 클래스

 

SecurityFilterChain Bean 정의를 통해 수행됩니다

 

이 방법으로 등록된 필터는 Spring Security의 보안 체인 일부로서 작동하며, 인증 및 인가와 같은 보안 관련 처리에 직접적으로 관여하게 됩니다

 

FliterChain Bean을 이용하면 Matcher 메커니즘을 이용한 요청 URL 별 인증/인가 적용 및 CustomFilter 적용을 달리할 수 있고, 다양한 정책들을 검토, 적용해 볼 수 있습니다

 

하지만 FilterChain에 대한 내용은 이번 글과는 조금 먼 주제이므로, 궁금하신 분들을 위해 예시 코드 링크만 남겨놓겠습니다

 


만약 Filter를 Bean으로도 등록(1)하고, FitlerChain에도 명시적으로 등록(2) 하면 어떤 일이 발생할까요?

 

정답은 해당 Filter를 두 번 거치게 됩니다.. 🫨


 

따라서 Filter를 생성하고 사용할 때 위 사항을 모르고 사용할 경우 Filter가 의도하지 않은 동작을 할 수 있습니다

 

 

개인적인 견해로는 (2) 번을 통해 원하는 FilterChain Bean에 원하는 Filter를 명시적으로 등록해 주면

 

코드 단에서 Filter들의 등록 및 순서를 한눈에 볼 수 있기에 (2) 번 방법을 선호하는 편입니다

 

 

어떤 방법을 채택해서 사용할지는 프로젝트 구성이나 Filter의 목적에 따라 달라질 수 있으니 상황에 맞는 방법을 적절히 채택해야 합니다

 

 

위 예시에 대한 Sample Code는 Github에 생성해 두었습니다

[Kotlin Code Github]

[Java Code Github]

 

 

이번 글에서는 Filter를 생성하고, 사용하는 방법에 대해 다뤄보았는데요

 

다음 글에서는 생성한 Filter를 사용하지 않는 방법에 대해 다뤄보겠습니다

 

 

반응형

댓글