ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 서블릿 필터
    SPRING 공부/서블릿 2022. 7. 10. 03:30

     

    필터란 말그대로 지정한 URL 패턴에 대해 거름막 역할을 해주는  기능이다.

    가장 중요한 것은 서블릿을 투과한 후 Filter를 거치고 그 이후에 스프링 영역(DispatcherServlet)에 접근하게 된다.

     

    주요 사용하는 용도로는 인코딩, XSS, CORS 등에 관한 설정을 주로 한다.

     

    이전 Spring에서는 web.xml에서 Java파일을 Filter를 추가하여 사용 할 수 있었는데,

    SpringBoot에서는 Annotation을 사용하여 Filter를 추가 할 수있다.

     

     

    먼저 사용할 CustomerFilter를 Servlet이 제공하는 Filter인터페이스를 구현하여 만든다.

    이때, chain.doFilter(request,response);를 해주지 않으면 다음 필터를 넘어가지 않게 되니 주의해서 등록하자

     

    @Component
    @Slf4j
    @RequiredArgsConstructor
    public class CustomTestFilter implements Filter {
    
        private final ExampleLog exampleLog;
    
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
          log.info("필터 init메서드 호출");
    
        }
    
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    
            log.info("필터 doFilter메서드 호출");
            log.info("필터 doFilter메서드 내의 exampleLog Bean 메서드 호출 시작 =====");
            exampleLog.testLog();
            log.info("필터 doFilter메서드 내의 exampleLog Bean 메서드 호출 끝 =====");
            Enumeration<String> attributeNames = request.getAttributeNames();
            ServletInputStream inputStream = request.getInputStream();
            StringBuilder sb  = new StringBuilder();
            String line = "";
            BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
            while((line = br.readLine()) !=null){
                if(line.indexOf(" ")>0){
                    sb.append("\n");
                }
                sb.append(line);
            }
            Iterator<String> names = attributeNames.asIterator();
            while(names.hasNext()){
                String next = names.next();
    
                log.info("next param = {}",next);
            }
            chain.doFilter(request,response);
        }
    
        @Override
        public void destroy() {
            log.info("필터 detroy메서드 호출");
        }
    }
    

    각 메서드 소개

    * Init() : 필터기능을 진행하기전 실행되는 메소드이다.

    * doFilter() : request, response ,chain을 파라미터로 받아서 필터작용을 해야하는 기능이 구현되어야하는 메소드이다.

    * destroy() : 필터 소멸후 실행되는 메소드이다.

     

     

    1. 기존 Web.xml방법

    <filter>
        <filter-name>CustomerFilter</filter-name>
        <filter-class>com.example.filterinterceptoraopdifference.filter</filter-class>
    </filter>
      
    <filter-mapping>
        <filter-name>CustomFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

       

    2. WebMvcConfigurer 구현체에 Bean등록하는방법

      FilterRegistrationBean 객체를 생성해서 Filter를 집어 넣어준 후 Bean을 등록하는 방법이다.

    
    @RequiredArgsConstructor
    @Configuration
    public class WebConfig implements WebMvcConfigurer {
    
        private final CustomTestFilter customFilter;
        private final CustomInterceptor customInterceptor;
    
        //필터를 Bean으로 등록해주기
        @Bean
        public FilterRegistrationBean customFilter(){
            FilterRegistrationBean<Filter>  filterRegistrationBean = new FilterRegistrationBean<>();
            filterRegistrationBean.setFilter(customFilter);
            filterRegistrationBean.setOrder(1);
            filterRegistrationBean.addUrlPatterns("/*");
            return filterRegistrationBean;
        }
    
    }
    

     

    3. @WebFilter 어노테이션으로 등록하는 방법

    1. 우선 Filter를 구현한 CustomFilter2에 WebFilter 어노테이션을 달아 주자.

    2. 그 다음 SpringBootApplication 어노테이션이 있는 곳에 가서  @ServletComponentScan을 어노테이션을 달아

        Servlet, Filter , Listener에 대한 스캔이 가능하게 해준다.

       Enables scanning for Servlet components (filters, servlets, and listeners)

    @Slf4j
    @WebFilter(urlPatterns = "/*")
    public class CustomFilter2 implements Filter {
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            log.info("CustomerFilter2 DoFilter");
            chain.doFilter(request, response);
        }
    }
    
    

    4.결과

    필터 적용 후 요청을 하였을 때의 로그

    이상으로 필터에 대해 알아 봤는데, 다음은 이와 비슷한 기능을 하는 인터셉터를 알아보기로 하자.

     

     

    ***** 추가로  Spring Bean을 호출할 수 없는지 확인해보기

    먼저 간단한 로그를 출력할 클래스를 Bean으로 등록하기

    @Slf4j
    @Component
    public class ExampleLog {
        public void testLog(){
            log.info("Spring Container내의 ExampleLog Bean의 Test Log 호출");
        }
    }

     

    Filter에서 의존성 주입 후 testLog 호출

     

    @Component
    @Slf4j
    @RequiredArgsConstructor
    public class CustomTestFilter implements Filter {
    
        private final ExampleLog exampleLog;
    
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
          log.info("필터 init메서드 호출");
        }
    
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            log.info("필터 doFilter메서드 호출");
            log.info("필터 doFilter메서드 내의 exampleLog Bean 메서드 호출 시작 =====");
            exampleLog.testLog();
            log.info("필터 doFilter메서드 내의 exampleLog Bean 메서드 호출 끝 =====");
            Enumeration<String> attributeNames = request.getAttributeNames();
            ServletInputStream inputStream = request.getInputStream();
            StringBuilder sb  = new StringBuilder();
            String line = "";
            BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
            while((line = br.readLine()) !=null){
                if(line.indexOf(" ")>0){
                    sb.append("\n");
                }
                sb.append(line);
            }
            Iterator<String> names = attributeNames.asIterator();
            while(names.hasNext()){
                String next = names.next();
    
                log.info("next param = {}",next);
            }
            chain.doFilter(request,response);
        }
    
        @Override
        public void destroy() {
            log.info("필터 detroy메서드 호출");
        }
    }

     

    결과 로그가 아래 처럼 나오게 된다.

     

    분명 Servlet Filter와 Spring Context는 분리되어있는걸로 알고 있었는데, 왜 Bean 주입이 가능한 걸까?

    스프링 부트 이전에 스프링에서는 org.springframework.web.filter.DelegatingFilterProxy 클래스가 Proxy패턴으로

    Bean 주입을 가능하게 해주었다.

     

    이후 스프링 부트에서는 내장톰캣을 사용하고 기본적으로 GenericFilterBean 을 상속한 클래스를 생성 하게되면서

    Servlet Filter와 Spring Bean을 연결하게 해주었다.

     

     

     

    지식 출처 : 13. 스프링부트 MVC - Filter 설정 · linked2ev

                        DelegatingFilterProxy (tistory.com)

    'SPRING 공부 > 서블릿' 카테고리의 다른 글

    Spring Interceptor  (0) 2022.07.13
    Spring Servlet의 원리  (0) 2022.01.19
Designed by Tistory.