본문 바로가기
spring

spring Filter

by 토망이 2023. 5. 20.

본 글에서는 모든 Rest API에 특정작업을 Rest API 종류와 무관하게 모두 적용할 수 있는 Spring Filter에 대해 알아보겠습니다. Controller에서 몇개의 메서드가 있던, Http Method 방식이 Get이건 Post이건 무관하게 Filter를 구축해놓으면 모든 Rest API에 대해 공통작업을 할 수 있는데, 주로 인증/인가 혹은 로깅에 사용할 수 있습니다.

핵심 코드는 chain.doFilter(request, response)이며,doFilter 이전의 코드는 Controller 실행 전, doFilter 이후의 코드는 Controller 실행 후 실행된다는 특징이 있습니다. doFilter를 호출해야 다음번 필터로 넘어가거나, 다음번 필터가 없으면 디스패처 서블릿에게 넘어가기 때문에 Filter내에서는 doFilter가 필수적으로 호출되어야 합니다.

 

코드를 통해 이해해보겠습니다.

- FilterConfig

TimeCheckFilter와 AuthorizationFilter를 선언하였으며, 각각 실행 시간 및 로깅을 담당하는 필터를 만드려 합니다. setOrder를 통해 필터의 실행 순서를 정할수 있습니다. (Client -> 1번 필터 -> 2번 필터 -> Controller -> 2번 필터 -> 1번 필터) 또한 추가적으로 .addUrlPatterns를 통해, 어떤 URL 요청에 필터를 적용할지를 설정할 수 있습니다. "/api/*"으로 설정하면, api/test 혹은 api/abc 이런식으로 들어오는 모든 Http 요청에 대해 필터를 적용할 수 있습니다.

@Slf4j
@Configuration
public class FilterConfig {

    @Bean
    public FilterRegistrationBean<Filter> timeCheckFilter() {
        FilterRegistrationBean<Filter> filterFilterRegistrationBean = new FilterRegistrationBean<>();
        filterFilterRegistrationBean.setFilter(new TimeCheckFilter());
        filterFilterRegistrationBean.setOrder(1);
        filterFilterRegistrationBean.addUrlPatterns("/api/*");
        return filterFilterRegistrationBean;
    }

    @Bean
    public FilterRegistrationBean<Filter> authorizationFilter() {
        FilterRegistrationBean<Filter> filterFilterRegistrationBean = new FilterRegistrationBean<>();
        filterFilterRegistrationBean.setFilter(new AuthorizationFilter());
        filterFilterRegistrationBean.setOrder(2);
        filterFilterRegistrationBean.addUrlPatterns("/api/*");
        return filterFilterRegistrationBean;
    }

}

 

- TimeCheckFilter

Filter 클래스를 implements 해야하며, public void doFilter를 Override해야 합니다. filterChain.doFilter 코드가 핵심코드이고 해당 코드 전은 Controller전에 수행, 해당 코드 후는 Controller 후에 수행됩니다.

@Slf4j
public class TimeCheckFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) servletRequest;

        String method = req.getMethod();
        StringBuffer requestURL = req.getRequestURL();

        long startTime = System.currentTimeMillis();
        try {
            log.info("TimeCheckFilter Method : {}, url : {},",method,requestURL);
            //핵심 코드 doFilter
            filterChain.doFilter(servletRequest,servletResponse);
        }
        catch (Exception e){
            throw e;
        }
        finally {
            long endTime = System.currentTimeMillis();
            long gap = endTime - startTime;
            log.info("FilterElaspedTime : {} ms",gap);
        }

    }
}

 

- AuthorizationFilter

마찬가지로 Filter 클래스를 implements 해야하며, public void doFilter를 Override해야 합니다. apikey라는 헤더를 통해 인증/인가를 구현해보았습니다. 아래 코드상으로는 "testkey123"으로 하드코딩 되어있지만, 실무에서는 DB에서 불러오는 식으로 구현해야합니다.

@Slf4j
public class AuthorizationFilter implements Filter {
    
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) servletRequest;

        String apiKey = req.getHeader("apikey");

        try{
            if(!"testkey123".equals(apiKey)) {
                throw new RuntimeException("filter invalid api key");
            }
            //핵심 코드 doFilter
            filterChain.doFilter(servletRequest,servletResponse);
        }
        catch (Exception e){
            throw e;
        }
        finally {
            log.info("AuthorizationFilter is applied");
        }
    }

}

 

필터가 정상적으로 적용되는지 확인하기 위해서, 아래와 같이 breakpoint를 설정하고 Run이 아닌 Debug를 통해 서버를 실행시켜야 합니다. 

 

Swagger을 통해 아무 restapi를 요청해보면(http://localhost:8080/api/RequestBody/test2), 아래와 같이 Swagger에서는 Loading이 뜨고 응답받지 않은 상태로 멈춰있고, Intellij Debugger를 통해 천천히 분석할 수 있습니다. Step Over(F8) 버튼을 누르면서 다음 필터까지 넘어가는 부분도 확인 가능합니다.

댓글