Java Spring Boot 006-2 | Interceptors & Filters

Yunny.Log ·2022년 3월 3일
0

Spring Boot

목록 보기
28/80
post-thumbnail

Interceptor와 Filter의 차이

  • 서버가 받는 모든 요청, 응답에 기능 적용하고 싶은 경우 ㅇ

  • 위는 스프링의 구조, 톰캣과 같은 was 따로 추가 필요, war 파일 사용

  • 위는 스프링부트의 구조, 톰캣이 둘러싸고 있는 모습
  • 위의 구조에 좀 더 보안사항을 추가하기 위해서 Filter이라는 아이 추가

  • 스프링 외부의 기능은 Filter

  • 그리고 스프링 내부에는 Interpretor 이라는 아이 추가
  • Interceptor에서 처리해주는 예외는 우리가 처리 가능
  • 하지만 filter에서 발생하는 예외는 우리가 처리 불가능

Filter

필터의 핵심 메서드 [출처] : (https://dololak.tistory.com/598)

  • doFilter()는 클라이언트의 요청이 있을때마다 매번 실행
  • ServletRequest와 ServletResponse 객체를 넘겨주기 때문에 이를 가지고 요청과 응답을 조작 가능
  • 그리고 FilterChain을 통해 조작 이후 요청을 원래 목적지인 서블릿으로 전달
 public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain) throws IOException, ServletException; 


출처: https://dololak.tistory.com/598 [코끼리를 냉장고에 넣는 방법]
  • doFilter() 함수를 구현
  • 이 doFilter아이는 HttpServletRequest, HttpServletRepsonse를 기본 인터페이스로 사용하지 않고, 얘네의 한 단계 조상인 ServletRequest, ServletResponse를 사용
  • 그리고 FilterChain변수를 사용해 filter의 전후 구분
  • 스프링 도착 전 처리 가능

(+) 출처

  • 필터는 애플리케이션의 HTTP 요청 및 응답을 가로채는 데 사용되는 개체입니다. - 필터를 사용하여 두 인스턴스에서 두 가지 작업을 수행 할 수 있습니다.
    • client의 요청을 가로채어 작업을 수행할 수 있습니다.
    • response 되기 전에 가로채어 작업을 수행
  • 필터는 스프링 컨텍스트 외부에서 request와 response의 해당하는 작업을 가로채어 공통 로직을 수행
  • 인터셉터와 AOP와 실행되는 시점이 다르니 각 상황에 맞추어 적절히 수행

(+) 출처

  • 필터(Filter)는 J2EE 표준 스펙 기능으로 디스패처 서블릿(Dispatcher Servlet)에 요청이 전달되기 전/후에 url 패턴에 맞는 모든 요청에 대해 부가작업을 처리할 수 있는 기능을 제공
  • doFilter 메소드
    doFilter 메소드는 url-pattern에 맞는 모든 HTTP 요청이 디스패처 서블릿으로 전달되기 전에 웹 컨테이너에 의해 실행되는 메소드이다. doFilter의 파라미터로는 FilterChain이 있는데, FilterChain의 doFilter 통해 다음 대상으로 요청을 전달하게 된다. chain.doFilter() 전/후에 우리가 필요한 처리 과정을 넣어줌으로써 원하는 처리를 진행

Interceptor

출처

  • 표준 스펙인 필터(Filter)와 달리 Spring이 제공하는 기술로써, 디스패처 서블릿(Dispatcher Servlet)이 컨트롤러를 호출하기 전과 후에 요청과 응답을 참조하거나 가공할 수 있는 기능을 제공한다. 즉, 웹 컨테이너에서 동작하는 필터와 달리 인터셉터는 스프링 컨텍스트에서 동작을 하는 것이다.
  • 디스패처 서블릿은 핸들러 매핑을 통해 적절한 컨트롤러를 찾도록 요청하는데, 그 결과로 실행 체인(HandlerExecutionChain)을 돌려준다. 그래서 이 실행 체인은 1개 이상의 인터셉터가 등록되어 있다면 순차적으로 인터셉터들을 거쳐 컨트롤러가 실행되도록 하고, 인터셉터가 없다면 바로 컨트롤러를 실행한다.
  • 인터셉터는 스프링 컨테이너 내에서 동작하므로 필터를 거쳐 프론트 컨트롤러인 디스패처 서블릿이 요청을 받은 이후에 동작하게 되는데, 이러한 과정을 그림으로 표현하면 다음과 같다.
코드를 입력하세요

[ 인터셉터(Interceptor)의 메소드 ][출처](https://mangkyu.tistory.com/173)
인터셉터를 추가하기 위해서는 org.springframework.web.servlet의 HandlerInterceptor 인터페이스를 구현(implements)해야 하며, 이는 다음의 3가지 메소드를 가지고 있다.

  • preHandle 메소드
  • postHandle 메소드
  • afterCompletion 메소드
public interface HandlerInterceptor {

    default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
        throws Exception {
        
        return true;
    }

    default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
        @Nullable ModelAndView modelAndView) throws Exception {
    }

    default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
        @Nullable Exception ex) throws Exception {
    }
}
  • postHandle의 인자로서 ModelAndView 들가있는데, 이 객체는 조작이 가능

(+) 출처

Filter와 Interceptor 비교

  • 필터는 DispatcherServlet 앞에서 먼저 동작하고,
  • 인터셉터는 DispatcherServlet에서 Controllr(Handler) 사이에서 동작

필터
웹 어플리케이션의 Context의 기능
스프링 기능을 활용하기에 어려움
일반적으로 인코딩, CORS, XSS, LOG, 인증, 권한 등 을 구현


인터셉터
스프링의 Spring Context의 기능이며 일종의 빈
스프링 컨테이너이기에 다른 빈을 주입하여 활용성이 좋음
다른 빈을 활용 가능하기에 인증, 권한 등을 구현

Filter 실습

filter 패키지 만들기

  • TransactionLogFilter 클래스 만들기
public class TransactionLogFilter implements Filter {
    private static final Logger logger = LoggerFactory.getLogger(TransactionLogFilter.class);

    @Override
    public void doFilter(
            ServletRequest request,
            ServletResponse response,
            FilterChain chain)
            throws IOException, ServletException {
        String requestUUID = UUID.randomUUID().toString().split("-")[0];//단순 구분자
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        logger.debug("[{}] start request : {} {}",
                requestUUID,
                httpServletRequest.getMethod(),
                httpServletRequest.getRequestURI()
            );
        //chain 호출하기 전까지가 filter 이 spring에 전달되기 전
        logger.info("* response status code : {}",((HttpServletResponse)response).getStatus());

        chain.doFilter(request, response); //chain 호출

        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        logger.info("* response status code : {}",((HttpServletResponse)response).getStatus());
        logger.info("[{}], send response : {} ",
                requestUUID, httpServletResponse.getStatus());
    }
  • chain 호출 전후로 status 코드가 변하게 됨

Interceptor 실습

public class HeaderLoggingInterceptor implements HandlerInterceptor {
    private static final Logger logger = LoggerFactory.getLogger(HeaderLoggingInterceptor.class);

    @Override
    // 이 부분에선 session 관리 등의 기능 수행
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //위 핸들러는 여기 도달하기 위한 핸들러
        //어떤 함수에 매핑수행할 건지 정해주기 위함
        HandlerMethod handlerMethod = (HandlerMethod) handler;//핸들러형변환
        logger.info("start processing of {}", ((HandlerMethod) handler).getMethod().getName());
        Enumeration<String> headerNames = request.getHeaderNames();
        while(headerNames.hasMoreElements()){
            String headerName = headerNames.nextElement();
            logger.trace("{}: {}", headerName, request.getHeader(headerName));
        }
        return true;
    }
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        Collection<String> headerNames = response.getHeaderNames();
        for (String headerName : headerNames){
            logger.trace("{} : {} ", headerName, response.getHeader(headerName));
        }
    }
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        logger.info("start processing of {}", handlerMethod.getMethod().getName());
        if (ex != null) logger.error("Exception occurred while processing", ex);
    }
    //Interceptor 다 만든 후엔 Configuration에 등록시켜줘야 함
}

DemoConfiguration.java

  • Interceptor 다 만든 후엔 Configuration에 등록
@Configuration
//WebMvcConfigurer 을 implement 수행
//addInterceptors 를 상속받기 위해서 상속받는 것
public class DemoConfig implements WebMvcConfigurer {
    private static final Logger logger = LoggerFactory.getLogger(DemoConfig.class);

    private final HeaderLoggingInterceptor headerLoggingInterceptor;

    public DemoConfig(
            @Autowired HeaderLoggingInterceptor headerLoggingInterceptor
    ){
        this.headerLoggingInterceptor=headerLoggingInterceptor;
    }
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry
                .addInterceptor(headerLoggingInterceptor)
                .addPathPatterns("post/**")
                .excludePathPatterns("/except/**");
    }
}

0개의 댓글