필터
서블릿 필터
사용자 인가 없이 URL 을 직접 호출해서 페이지에 접속하는 것을 막아주는 역할을 한다.
서블릿이 제공하는 기술로, 인터셉터와 비슷하지만 인터셉터가 더 편리하고, 정교하다.
필터 흐름
HTTP 요청 > WAS > 필터 > 서블릿 > 컨트롤러
필터가 호출된 다음 디스패처 서블릿이 호출되기 때문에,
필터를 통해 요청을 검토하여 적절하지 않은 요청이면 디스패처 서블릿을 호출하지 않는다.
필터 체인
HTTP 요청 > WAS > 필터1 > 필터2 > 필터3 > 서블릿 > 컨트롤러
또한 필터는 체인으로 구성되어 중간에 필터를 추가할 수 있다.
필터 인터페이스
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()` : 필터 초기화 메서드, 서블릿 컨테이너 생성 시 호출
`doFilter()` : 요청이 올 때마다 해당 메서드 호출
`destroy()` : 필터 종료 메서드, 서블릿 컨테이너 종료 시 호출
요청 로그 필터
@Slf4j
public class LogFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
log.info("log filter init");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
String requestURI = httpRequest.getRequestURI();
String uuid = UUID.randomUUID().toString();
try {
log.info("REQUEST [{}][{}]", uuid, requestURI);
chain.doFilter(request, response);
} catch (Exception e) {
throw e;
} finally {
log.info("RESPONSE [{}][{}]", uuid, requestURI);
}
}
@Override
public void destroy() {
log.info("log filter destroy");
}
}
`doFilter(ServletRequest request, ServletResponse response, FilterChain chain)`
: 여기서 ServletRequest request 는 HTTP 요청이 아닌 경우까지 고려한 인터페이스이기 때문에 다운케스팅 하여 사용
`UUID` : Http 요청을 구분하기 위해 요청당 임의의 uuid 생성
`chain.doFilter(request, response)` : 다음 필터 있으면 필터 호출, 없으면 서블릿 호출 (없으면 다음 단계 진행 안 됨)
> 참고 <
doFilter 에서 다운캐스팅이 가능한 이유는 ??
다운 캐스팅이 가능한 이유는, 파라미터로 들어온 request 는 ServletRequest 타입이지만, 실제로 서블릿 컨테이너에서 전달하는 객체는 그의 자식인 HttpServletRequest 의 구현체다. 따라서 원래 HttpServletRequest 타입이므로 다운캐스팅이 가능하다.
@Configuration
public class WebConfig {
@Bean
public FilterRegistrationBean logFilter() {
FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>();
filterRegistrationBean.setFilter(new LogFilter());
filterRegistrationBean.setOrder(1);
filterRegistrationBean.addUrlPatterns("/*");
return filterRegistrationBean;
}
}
필터를 등록해야 사용할 수 있다.
이때, 스프링부트를 사용하면 `FilterRegistrationBean` 을 사용하여 등록하면 된다.
`setFilter(new LogFilter())` : 등록할 필터 지정
`setOrder()` : 필터의 순서 (낮을수록 먼저 동작)
`addUrlPatterns()` : 필터를 적용할 URL 패턴 지정 (여러 개 가능)
인증 체크 필터
@Slf4j
public class LoginCheckFilter implements Filter {
private static final String[] whitelist = {"/", "/members/add", "/login", "/logout","/css/*"};
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
String requestURI = httpRequest.getRequestURI();
HttpServletResponse httpResponse = (HttpServletResponse) response;
try {
log.info("인증 체크 필터 시작 {}", requestURI);
if (isLoginCheckPath(requestURI)) {
log.info("인증 체크 로직 실행 {}", requestURI);
HttpSession session = httpRequest.getSession(false);
if (session == null || session.getAttribute(SessionConst.LOGIN_MEMBER) == null) {
log.info("미인증 사용자 요청 {}", requestURI);
httpResponse.sendRedirect("/login?redirectURL=" + requestURI);
return;
}
}
chain.doFilter(request, response);
} catch (Exception e) {
throw e;
} finally {
log.info("인증 체크 필터 종료 {}", requestURI);
}
}
private boolean isLoginCheckPath(String requestURI) {
return !PatternMatchUtils.simpleMatch(whitelist, requestURI);
}
}
`whitelist = {"/", "/members/add", "/login", "/logout", "/css/*"};`
: 인증과 무관하게 항상 허용할 URL 경로. 나머지 경로에 인증 체크 로직 실행
`httpResponse.sendRedirect("/login?redirectURL=" + requestURI);`
: 미인증 사용자를 로그인 화면으로 리다이렉트
: 현재 요청 경로 requestURI 를 쿼리 파라미터로 함께 전달하여 로그인 성공 시 해당 경로로 이동할 수 있도록 함
`return;`
: 이후 서블릿, 컨트롤러가 호출되지 않고 요청이 끝남
@Bean
public FilterRegistrationBean loginCheckFilter() {
FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>();
filterRegistrationBean.setFilter(new LoginCheckFilter());
filterRegistrationBean.setOrder(2);
filterRegistrationBean.addUrlPatterns("/*");
return filterRegistrationBean;
}
`setFilter(new LoginCheckFilter())` : 등록할 필터 지정
`setOrder()` : 필터의 순서 (낮을수록 먼저 동작)
`addUrlPatterns()` : 필터를 적용할 URL 패턴 지정 (여러 개 가능)
출처 | 스프링 MVC 2(김영한) - 인프런
'💠프로그래밍 언어 > Java' 카테고리의 다른 글
[Spring] 컨트롤러 공통 작업 자동화 (ArgumentResolver 의 활용) (0) | 2025.04.04 |
---|---|
[Spring] 인터셉터란? + 요청 로그 인터셉터, 인증 체크 인터셉터 예시 (0) | 2025.04.04 |
[Spring] 쿠키/세션으로 로그인 처리하기 (세션 저장소 만들기, HttpSession) (0) | 2025.04.03 |
[Spring] Bean Validation 사용하여 간편하게 검증 로직 추가하기 ! (0) | 2025.04.03 |
[Spring] 필드/글로벌 검증하기 (BindingResult, MessageResolver, @Validated) (1) | 2025.04.02 |