[Spring] Filter, Interceptor의 차이

eunhye_·2023년 3월 22일
0

Spring

목록 보기
4/5
post-custom-banner

Filter란?

  • 웹 애플리케이션에서 들어오는 요청과 나가는 응답을 가로채고 수정하는 데 사용됨
  • DispatcherServlet이 실행되기 전에 실행되며 웹 컨테이너에서 관리됨
  • Interceptor와는 달리 Request, Response 객체를 수정할 수 있음 (아래 Filter Interface 참고)
  • Java Servlet API의 표준 기능
  • 인증, 로깅, XSS 방어, 이미지 및 데이터 압축, 인코딩/디코딩 요청 및 응답, 스프링과 무관하게 전역적으로 처리해야하는 작업을 처리하는데 사용됨

Interceptor란?

  • DispatcherServlet이 컨트롤러를 호출하기 전/후로 스프링컨 텍스트 내부에서 컨트롤러에 관한 요청과 응답에 대해 처리
  • 스프링 컨텍스트 외부에 존재하는 필터와는 달리 인터셉터는 스프링 컨텍스트 내부에 존재하기 때문에 스프링의 모든 빈 객체에 접근이 가능
  • HandlerInterceptor 인터페이스를 사용하여 정의할 수 있음
  • 세부적인 보안 및 인증, 인가 작업, 로깅, Controller에 넘겨주는 데이터 가공

Filter 메서드

package javax.servlet;

import java.io.IOException;

public interface Filter {

    public default void init(FilterConfig filterConfig) throws ServletException {}

    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException;

    public default void destroy() {}
}

👉 init()

필터 인스턴스 초기화 메서드

서블릿 컨테이너는 필터를 인스턴스화 한 후 한 번 init 메서드를 호출

👉 doFilter()

헤더, 매개변수 또는 속성을 추가하거나 수정하는 등 요청 및 응답 객체를 수정할 수 있음 (Interceptor는 객체 수정 못함)

FilterChain을 사용하여 체인의 다음 필터 또는 요청을 처리하는 서블릿을 호출할 수 있음

👉 destroy()

서블릿 컨테이너는 doFilter메서드 내의 모든 쓰레드가 종료되거나 설정한 제한 시간이 지난 후에 destroy 메서드를 한 번 호출하며, 이후에는 해당 필터의 인스턴스로는 doFilter를 호출하지 않는다.

Filter 구현 예시

@WebFilter(urlPatterns = {"/api/*"}, asyncSupported = true)
@Component
@RequiredArgsConstructor
public class AccessLogFilter extends OncePerRequestFilter {

    private final AccessLogger accessLogger;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
        long stime = System.currentTimeMillis();
        try {
            chain.doFilter(request, response);
        } finally {
            long etime = System.currentTimeMillis();
            long elapsed = etime - stime;
            accessLogger.log(request, response, elapsed);
        }
    }
}

Interceptor 메서드

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 {
   }

}

👉 preHandle()

요청이 컨트롤러에 의해 처리되기 전에 호출됨
요청을 계속 처리해야 하는지 또는 중지해야 하는지를 나타내는 boolean 값을 리턴 (false면 작업 중단)

👉 postHandle()

요청이 컨트롤러에 의해 처리된 후 view가 렌더링되기 전에 호출됨
model과 view를 수정하는데 사용 가능

👉 afterCompletion()

뷰가 렌더링된 후 호출됨

로깅, 리소스 반환과 같은 정리 작업을 수행하는 데 사용

JWT 인증 Interceptor 구현 예시

@Component
@RequiredArgsConstructor
public class JwtTokenInterceptor implements HandlerInterceptor {

    private final JwtTokenProvider jwtTokenProvider;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {

        System.out.println("JwtToken 호출");
        String accessToken = request.getHeader("ACCESS_TOKEN");
        System.out.println("AccessToken:" + accessToken);
        String refreshToken = request.getHeader("REFRESH_TOKEN");
        System.out.println("RefreshToken:" + refreshToken);

        if (jwtTokenProvider.isValidAccessToken(accessToken) && jwtTokenProvider.isValidRefreshToken(refreshToken)) {
            return true;
        }else{
            throw new RequestException(ErrorCode.JWT_BAD_TOKEN_401);
        }
    }

}
post-custom-banner

0개의 댓글