서블릿 필터

UkJJang·2021년 9월 28일
0

인프런 김영한 님의 https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-2/dashboard 강의를 보며 학습했습니다.

필터

  • 필터는 서블릿이 제공하는 기능이다.
  • 공통 관심사를 해결하기 위해서는 HTTP의 헤더나 URL정보들이 필요한데 서블릿 필터나 스프링 인터셉터는 HttpServletRequest를 제공해준다.

서블릿 필터

  • 서블릿 필터는 수문장 역할을 담당하는 기능이다.

필터의 흐름
HTTP 요청 > WAS > 필터 > 서블릿(디스패처 서블릿) > 컨트롤러

필터제한
HTTP 요청 > WAS > 필터 > 서블릿 > 컨트롤러 // 로그인 사용자일 경우
HTTP 요청 > WAS > 필터( 적절하지 않은 요청이라 판단, 서블릿 호출 X) // 비 로그인 사용자일 경우

필터체인
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() : 고객의 요청이 올 때 마다 해당 메서드가 호출된다. 필터의 로직을 구현하면 된다.
  • destory() : 필터 종료 메서드, 서블릿 컨테이너가 종료될 때 호출된다.

서블릿 필터 - 요청 로그

@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 {
        log.info("log filter doFilter");

        // 기능이 많지 않기때문에 다운캐스팅
        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");
    }
}

서블릿 필터 스프링에 추가하기

@Configuration
public class WebConfig implements WebMvcConfigurer {

    public FilterRegistrationBean logFilter() {
        FilterRegistrationBean<Filter> filterFilterRegistrationBean = new FilterRegistrationBean<>();
        filterFilterRegistrationBean.setFilter(new LogFilter()); // 만든 필터 넣어주고
        filterFilterRegistrationBean.setOrder(1); // 필터 우선순위 정해주고
        filterFilterRegistrationBean.addUrlPatterns("/*"); // 모든 경로에 다 적용한다.

        return filterFilterRegistrationBean;
    }
}

logback mdc도 한번 검색해보자.

로그인 체크 필터 작성해보기


@Slf4j
public class LoginFilter implements Filter {

    // 들어올수 있는 경로
    private static final String[] whiteList = {"/", "/members/add", "/login", "/logout", "/css/*"};

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        Filter.super.init(filterConfig);
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

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

        HttpServletResponse httpServletResponse = (HttpServletResponse) response;

        try{
            log.info("인증체크 필터 시작 {}",requestURI);

            if(isLoginCheckPath(requestURI)) {
                log.info("인증 체크 로직 실행 {}", requestURI);

                HttpSession session = httpServletRequest.getSession(false);
                if (session == null || session.getAttribute(SessionConst.LOGIN_MEMBER)==null) {
                    log.info("미인증 사용자가 요청했다 !{}", requestURI);
                    // 로그인으로 리다이렉트
                    httpServletResponse.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);
    }

    @Override
    public void destroy() {
        Filter.super.destroy();
    }

}

리다이렉트 부분은 컨트롤러에서 @RequestParam(defaultValue="/") String redirectURL 파라미터로 받아서 처리해줘야 한다.

profile
꾸준하게 성실하게

0개의 댓글