Filter
- Filter란 Web Application에서 관리되는 영역으로써 Spring Boot Framework에서 Client로부터 오는 요청/응답에 대해서 최초/최종 단계의 위치에 존재함(ObjectMapper가 적용되기도 전임. 객체 매핑 전)
- 이를 통해서 요청/응답의 정보를 변경하거나 Spring에 의해서 데이터가 변환되기 전의 순수한 Client의 요청/응답 값을 확인 할 수 있음
- 유일하게 ServletRequest, ServletResponse의 객체를 변환 할 수 있음
- 필터는 다수의 서블릿이나 다른 Java EE 웹 컴포넌트가 어떤 일반적인 기능, 예를 들어 인증, 로깅, 암호화와 같은 기능을 적용하고자 할 때 사용됨
- Authentication Filters
- Logging and Auditing Filters
- Image conversion Filters
- Data compression Filters
- Encryption Filters
- Tokenizing Filters
- Filters that trigger resource access events
- XSL/T filters
- Mime-type chain Filter
- 이를 선/후 처리 함으로써 Service business logic과 분리시킴
- Filter가 제일 앞단에 있고 그다음이 Intercepter, 그 다음이 AOP 순서대로 적용이 됨
- Filter는
java.servlet.Filter
를 구현해야 함
@WebFilter("/*")
public class LoggingFilter implements Filter {
private FilterConfig filterConf = null;
public void init(FilterConfig filterConfig) { this.filterConf = filterConf; }
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {
String userAddy = request.getRemoteHost();
filterConf. getServletContext().log("Visitor User IP: " + userAddy);
chain.doFilter(request, response);
}
@Override
public void destroy() { throw new UnsupportedOperationException("Not supported yet.");}
}
Filter Life Cycle
- 서블릿 컨테이너에 의해 필터 생성
- 각 요청 응답 마다
doFilter()
적용
- 서블릿 컨테이너가
destroy()
를 호출함. (컨테이너가 꺼질 때)
Filter Chain
- 필터는 그 다음 필터를 호출하기 위해 필터 체인을 이용함
- 여러 필터가 있을 때, 실행 순서는 web.xml의 configuration에 따름
- 모든 필터와 targe resource는 하나의 스레드에 있게 됨
- 모든 필터는 하나의 공통된 요청 객체를 공유함
Filter matching pattern
- URL 패턴은 US-ASCII 문자의 부분집합을 포함함. 다른 값들은 escape처리 되어야함
- 필터는 URL만 고려해서 해당 필터를 적용함
- Exact matching
- Path matching
/admin/*
- Type matching
.css
Filter 예제 코드
import java.servlet.*;
@Slf4j
@Componenet
// 만약 특정한 api endpoint에 대해서만 filter를 적용하고 싶을때 Component지우고
// @WebFilter(urlPatterns="/api/user/*")
// 그리고 @SpringBootApplication 에 @ServletComponentScan 붙여주기
public class GlobalFilter implements Filter{
@Override
public void doFilter(ServletRequest request,
ServletResponse response, FilterChain chain) throws IOException, ServletException{
// 전처리
HttpServletRequest httpServletRequest = (HttpServletRequest)request;
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
String url = httpServletRequest.getRequestURI();
BufferedReader br = httpServletRequest.getReader();
br.lines().forEach(line ->{
log.info("url : {}, line : {}", url, line);
}); // 근데이렇게 하면 Controller에서 바디를 더이상 읽지를 못함
// HttpServletRequest -> ContentCachingRequestWrapper
// HttpServletResponse -> ContentCachingResponseWrapper
chain.doFilter(request, response); // 이 과정이 진행되면 response가 만들어짐
// 후처리
String reqContent = new String(httpServletRequest.getContentAsByteArray());
log.info("request url : {}, request body : {}", url, reqContent);
String resContent = new String(httpServletResponse.getContentAsByteArray());
int httpStatus = httpServletResponse.getStatus();
httpServletResponse.copyBodyToResponse(); // 앞에서 읽어서 반환이 안됨.복사해주어야함
}
}
ContentCachingRequestWrapper
- request 데이터가 읽히면 해당 데이터의 byte array를 ContentCachingRequestWrapper에 저장해두고 해당 byte array를 계속 반환함(데이터를 여러번 읽는 것이 아님!!. 데이터는 한번만 읽고 byte array를 반환하는것)
- 그래서 데이터가 읽혀야지만
getContentAsByteArray()
로 데이터 반환이 가능