Spring MVC Request Life Cycle - Filter

출처 : https://all-record.tistory.com/164

목차

Filter

  • Filter는 Spring Context 이전에 실행되기 때문에 Spring자체와는 무관하며 Web Application에 등록이 된다.
  • 요청이 DispatcherServlet에 도착하기 전에 처리를 하거나 요청이 끝나고 응답할 때 마지막으로 처리를 하는 역할을 한다.
  • 주로 인코딩, 요청에 대한 인증, 권한 체크 와 같은 요청에 대한 전역적인 처리를 할 때 사용한다.

Filter 인터페이스

  • init
    • 필터 인스턴스 초기화
  • doFilter
    • 실제 로직 처리(Filter Chain을 따라 다을 Filter로 움직인다.)
  • destroy
    • 필터 인스턴스 종료
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() {}
}

Filter Chain

Filter는 Client와 Servlet사이에 존재해 Client가 보내는 요청정보를 가공해 Servlet으로 보내고 Servelt이 보내는 응답정보를 가공해 Client에게 전달하는 역할을 한다. Filter는 일반적인 경우 1개가 존재하지만 여러개의 필터를 모아 Filter Chain을 형성할 수 있다.

Login Filter 생성하기

@Slf4j
public class LoginFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
log.info("log filter init");

Filter.super.init(filterConfig);
}

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
log.info("log filter doFilter");

HttpServletRequest httpRequest = (HttpServletRequest) request;
String requestURI = httpRequest.getRequestURI();

String uuid = UUID.randomUUID().toString();

try{
log.info("REQUEST [{}][{}]", uuid, request);
chain.doFilter(request, response);
}catch (Exception e){
throw e;
}finally {
log.info("RESPONSE [{}][{}]", uuid, requestURI);
}
}

@Override
public void destroy() {
log.info("log filter destroy");
Filter.super.destroy();
}
}

Filter 등록하기

스프링 부트에서는 내장 톰캣을 가지고 있어 자바코드로 Filter를 Java Code를 이용해 손 쉽게 등록할 수 있다. FilterRegistrationBean 객체를 생성해 만드러준 Filter 를 Bean 으로 등록한다.

@Configuration
public class WebConfig {

@Bean
public FilterRegistrationBean logFilter(){
FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<Filter>();
filterRegistrationBean.setFilter(new LoginFilter());
filterRegistrationBean.setOrder(1);
filterRegistrationBean.addUrlPatterns("/*");

return filterRegistrationBean;
}
}
@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);
//로그인으로 redirect
httpResponse.sendRedirect("/login?redirectURL=" + requestURI);
return;
}
}

chain.doFilter(request, response);
} catch (Exception e) {
throw e; //예외 로깅 가능 하지만, 톰캣까지 예외를 보내주어야 함
} finally {
log.info("인증 체크 필터 종료 {} ", requestURI);
}

}

/**
* 화이트 리스트의 경우 인증 체크X
*/
private boolean isLoginCheckPath(String requestURI) {
return !PatternMatchUtils.simpleMatch(whitelist, requestURI);
}
}
@Configuration
public class WebConfig {

@Bean
public FilterRegistrationBean logFilter(){
FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>();
filterRegistrationBean.setFilter(new LoginFilter());
filterRegistrationBean.setOrder(1);
filterRegistrationBean.addUrlPatterns("/*");

return filterRegistrationBean;
}

@Bean
public FilterRegistrationBean logCheckFilter(){
FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>();
filterRegistrationBean.setFilter(new LoginCheckFilter());
filterRegistrationBean.setOrder(2);
filterRegistrationBean.addUrlPatterns("/*");

return filterRegistrationBean;
}
}

로그인 후 Redirect 해주기

@PostMapping("/login")
public String loginV4(@Valid @ModelAttribute LoginForm form
, BindingResult bindingResult
, HttpServletRequest request
, @RequestParam(defaultValue = "/") String redirectURL
){
log.info("Request Params : {}", form);

if(bindingResult.hasErrors()){
return "login/loginForm";
}

Member loginMember = loginService.login(form.getLoginId(), form.getPassword());

if(loginMember == null){
bindingResult.reject("loginFail", "아이디 또는 비밀번호가 맞지 않습니다.");
return "login/loginForm";
}

// 로그인 설공 처리 TODO
// 세션이 있으면 있는 세션 반환, 없으면 신규 세션을 생성
HttpSession session = request.getSession();
// 세션에 로그인 회원 정보를 보관
session.setAttribute(SessionConst.LOGIN_MEMBER, loginMember);

return "redirect:" + redirectURL;
}
Share