(Spring)필터와 인터셉터
필터와 인터셉터
필터
필터란?
- DispatcherServlet에 요청이 전달되기 전/후에 url 패턴에 맞는 모든 요청에 대 부가작업을 처리할 수 있는 기능을 제공하는 J2EE 표준 스팩 기능
- DispatcherServlet이 Spring에 가장 앞단에 존재하는 Controller 이기 때문에 필터는 스프링의 밖에서 처리가 된다.
- 스프링 컨테이너에서 관리되는 것이 아니라 톰캣과 같은 웹 컨테이너 안에서 관리된다.
필터의 메서드
1
2
3
4
5
6
7
8
9
10
11
import javax.servlet.Filter; //Filter라는 이름을 가진 인터페이스가 몇개 더 있다. 헤깔리지말고 이걸 import해야한다.
public class FirstFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException { ... }
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {}
@Override
public void destroy() { ... }
- import: javax.servlet.Filter
- init: init 메서드는 필터 객체를 초기화하고 서비스에 추가하는 기능을 하는 메서드,
웹 컨테이너
가 1회init 메서드
를 호출하여 필터 객체를 초기화 하면 이후의 요청들은doFilter
를 통해 처리된다. - doFilter: URL 패턴에 맞는 모든 HTTP 요청이 디스패처 서블릿으로 전달되기 전에 웹 컨테이너에 의해 실행되는 메서드, chain.doFilter() 전/후에 작성되어 있는 필요한 로직을 넣어줌으로 원하는 처리를 진행할 수 있다. - 파라미터 - FilterChain: FilterChain의 doFilter를 통해 다음 대상으로 요청을 전달
- destroy: 필터 객체를 서비스에서 제거하고 사용하는 자원을 반환하기 위한 메서드, 웹서버에 의해 1번 호출되며 이후 doFilter에 의해 처리되지 않는다.
필터 Config
- 예전에는 필터가 스프링 컨테이너에서 관리되는 것이 아니라
Bean
으로 등록하는게 불가능 했다고 한다. 하지만 지금은Bean
으로 등록이 가능하며, 더욱 편리하게 사용할 수 있게 되었다.
1
2
3
4
5
6
public class MyWebApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
public void onStartup(ServletContext servletContext) throws ServletException {
super.onStartup(servletContext);
servletContext.addFilter("FirstTestFilter", DelegatingFilterProxy.class);
}
}
Spring
에서 Filter를 등록하는 방법
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Configuration
public class MvcConfiguration implements WebMvcConfigurer {
@Bean
public FilterRegistrationBean<FirstTestFilter> firstFilterRegister() {
FilterRegistrationBean<FirstTestFilter> registrationBean = new FilterRegistrationBean<>(new FirstTestFilter());
registrationBean.setOrder(1);
return registrationBean;
}
@Bean
public FilterRegistrationBean<SecondTestFilter> secondFilterRegister() {
FilterRegistrationBean<SecondTestFilter> registrationBean = new FilterRegistrationBean<>(new SecondTestFilter());
registrationBean.setOrder(2);
return registrationBean;
}
}
Spring Boot
에서 Filter를 등록하는 방법- 여러개의 필터를 사용할때는
setOrder()
를 사용해서 순서를 정해 줄 수 있다.
필터 이미지
- 필터의 작동 과정을 이미지로 표현
인터셉터
인터셉터란?
- Spring이 제공하는 기술, 디스패처 서블릿이 컨트롤러를 호출하기 전과 후에 요청과 응답을 참조하거나 가공할 수 있는 기능을 제공
인터셉터의 메서드
1
2
3
4
5
6
7
8
9
10
11
12
public class FirstTestInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { }
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { }
}
- imporit: org.springframework.web.servlet
- preHandle: 컨트롤러가 호출되기 전에 실행되며, 컨트롤러 이전에 처리해야하는 전처리 작업이나 요청정보를 가공, 추가하는데 사용된다.
- postHandle: 컨트롤러 호출된 후에 실행된다. 컨트롤러 이후에 처리해야 하는 후처리 작업이 있을때 사용
- afterCompletion: 모든 뷰에서 최종 결과를 생성하는 일을 포함, 모든 작업이 완료된 후에 실행된다. 요청 처리 중에 사용한 리소스를 반환할 때 사용하기에 적합하다.
인터셉터 Config
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Configuration
public class MvcConfiguration implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(interceptor)
.addPathPatterns("/")
.addPathPatterns("/member/")
.excludePathPatterns("/login").order(1);
registry.addInterceptor(subInterceptor)
.addPathPatterns("/")
.excludePathPatterns("/login2").order(2);
}
}
- addPathPatterns: 인터셉터가 동작할 URL 패턴
- excluedPathPatterns: 인터셉터가 동작하지 않을 URL 패턴
- order: 다중 인터페이스 사용 시 순서를 정해준다.
인터셉터 이미지
- 인터셉터가 위치하는 곳과 처리순서를 이미지로 간략하게 표현
필터(Filter)와 인터셉터(Interceptor) 차이 및 용도
대상 | 필터(Filter) | 인터셉터(Intercepter) |
---|---|---|
관리되는 컨테이너 | 웹 컨테이너 | 스프링 컨테이너 |
Request/Response</br>객체 조각 가능 여부 | 가능 | 불가능 |
용도 | - 공통된 보안 및 인증/인가 관련 작업 - 모든 요청에 대한 로깅 또는 감사 - 이미지/데이터 압축 및 문자열 인코딩 - Spring과 분리되어야 하는 기능 | - 세부적인 보안 및 인증/인가 공통작업 - API 호출에 대한 로깅 또는 감사 - Controller로 넘겨주는 데이터 가공 |
Request/Response 객체 조작 가능 여부
- 필터는 Request/Response를 조작할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class FirstTestFilter implements Filter {
private final Logger log = LoggerFactory.getLogger(this.getClass());
@Override
public void init(FilterConfig filterConfig) throws ServletException {
log.info("FirstFilter 생성: {}", this.getClass());
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
log.info("FirstFilter 실행: {}", this.getClass());
chain.doFilter(request, response);
((HttpServletResponse) response).sendError(HttpServletResponse.SC_BAD_REQUEST); // response를 조작하여, 어떤 request가 들어오던 400을 반환
log.info("FirstFilter 종료: {}", this.getClass());
}
@Override
public void destroy() {
log.info("FirstFilter 삭제: {}", this.getClass());
}
}
- 위에 보이는 코드처럼 response를 조작하여, 어떤 request가 들어오던 400을 반환시킬 수 있다.
- 이렇게 response와 request를 조작할 수 있는것이 인터셉터와 가장 큰 차이점이다.
- 인터셉터는 필터와 처리 과정이 다르기 때문에 Request/Response객체를 조작하여 넘겨줄수 없다.
- 인터셉터는 디스패처 서블릿이 여러 인터셉터의 목록을 가지고, 반복문을 통해 순차적으로 실행시킨다. 그 후 true를 반환하면 다음 인터셉터가 실행되거나 컨트롤러로 요청이 전달된다.(false가 반환되면 요청이 중단된다.)
1
2
3
4
5
6
7
8
9
10
public class FirstTestInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (...) {
...
return true;
}
return false;
}
}
- 위에 보이는것처럼 true와 false 두가지만 반환이 가능하고, 그 결과로 다음 인터셉가 실행되거나, 컨트롤러로 요청이 전달되기 때문에 request나 response의 조작이 불가능하다.
필터의 용도 및 예시
1
2
3
4
- 공통된 보안 및 인증/인가 관련 작업
- 모든 요청에 대한 로깅 또는 감사
- 이미지/데이터 압축 및 문자열 인코딩
- Spring과 분리되어야 하는 기능
- 필터에서는 기본적으로 스프링과 무관하게 전역적으로 처리해야 하는 작업들을 처리할 수 있다.
- 이미지나 데이터의 압축, 문자열 인코딩과 같이 웹 애플리케이션에 전박적으로 사용되는 기능을 구현하기 좋다.
인터셉터의 용도 및 예시
1
2
3
- 세부적인 보인 및 인증/인가 공통작업
- API 호출에 대한 로깅 또는 감사
- Controller로 넘겨주는 데이터의 가공
- 클라이언트의 요청과 관련되어 전역적으로 처리해야 하는 작업들을 처리할 수 있다.
- 특정 그룹의 사용자마다 사용할 수 있는 기능을 나눌때 사용하면 좋다.
- JWT토큰 정보를 파싱해서 컨트롤러에 사용자의 정보를 제공하는 등 컨트롤러로 넘겨주기 위한 정보를 가공하기 용이하다.
REFRENCE
This post is licensed under CC BY 4.0 by the author.