mini_project/회원가입, 로그인 구현하기

[servlet] 필터 (filter) 개념 / 흐름 / 예시

개발자 배찌 2021. 12. 16. 15:26
728x90

필터(filter)

필터는 서블릿이 지원하는 수문장이다.

 

필터 흐름

HTTP 요청 -> WAS -> 필터 -> 서블릿 -> 컨트롤러

 

필터 제한 

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

 

 필터 체인

필터는 체인으로 구성되는데, 중간에 필터를 자유롭게 추가할 수 있다. 

HTTP요청 -> WAS -> 필터1 -> 필터2 -> 필터3 -> 서블릿 -> 컨트롤러

 

코드로 보는 필터!!!!!!

Q1 . 모든 요청을 로그로 남기는 필터를 개발.

 

필터 인터페이스를 구현하고 등록하면 서블릿 컨테이너가 필터를 싱글톤 객체로 생성하고 관리한다.

 

 

로그필터 LogFilter.java

@Slf4j
public class LogFilter implements Filter {

     //init() : 필터 초기화 메서드, 서블릿 컨테이너가 생성될 때 호출
     @Override
     public void init(FilterConfig filterConfig) throws ServletException {
          log.info("log filter init");
     }

     //doFilter() : 고객의 요청이 올 때 마다 해당 메서드가 호출. 필터의 로직을 구현하는 곳
     @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);
           }
}
      
      //destroy() : 필터 종료 메서드. 서블릿컨테이너가 종료될 때 호출
      @Override
      public void destroy() {
              log.info("log filter destroy");
       }
}

 

필터 설정 - WebConfig.java

필터를 등록하는 방법은 여러가지 있지만, 스프링 부트를 사용한다면 filterRegistrationBean를 사용해서 등록하면 됨.

@Configuration
public class WebConfig {
     @Bean
     public FilterRegistrationBean logFilter() {
             FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>();

            filterRegistrationBean.setFilter(new LogFilter());         //등록할 필터 지정
            filterRegistrationBean.setOrder(1);                         //필터는 체인으로 동작한다. 순서가 필요함
            filterRegistrationBean.addUrlPatterns("/*");              //필터를 적용할 URL 패턴을 지정

            return filterRegistrationBean;
      }
}

 

 

Q2 . 로그인 인증체크 필터 개발

@Slf4j
public class LoginCheckFilter implements Filter {
          private static final String[] whitelist = {"/" , "/login" , "/logout" , "/css/*"};

         @Override
         public void doFilter(ServletRequest request, ServletResponse response, FilterChain chian) throws IOException, ServletException {
             HttpServletRequest httpRequest = (HttpServletRequest) request ;
             String requestURI = httpRequest.getRequestURI();

             HttpServletResponse httpResponse = (HttpServletResponse) response;

            try {
                 log.info("로그인 인증체크 필터 시작 {} " , requestURI);
                 //화이트리스트인 경우에, 인증체크 x
                 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);
          }
}

 

필터 설정 - WebConfig.java

@Configuration
public class WebConfig {
     @Bean
     public FilterRegistrationBean logFilter() {
             FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>();

            filterRegistrationBean.setFilter(new LoginCheckFilter());         //등록할 필터 지정
            filterRegistrationBean.setOrder(2);                         //필터는 체인으로 동작한다. 순서가 필요함
            filterRegistrationBean.addUrlPatterns("/*");              //필터를 적용할 URL 패턴을 지정

            return filterRegistrationBean;
      }
}

 

RedirectURL 처리 - LoginController

**로그인 이후 redirect 처리

@PostMapping("/login");
public String login (
         @Valid @ModelAttribute LoginForm form, BindingResult bindingResult,
         @RequestParam(defaultValue = "/") String redirectURL,
         HttpServletRequest request) {

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

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

     // 로그인 성공 처리
     HttpSession session = request.getSession();
     session.setAttribute(SessionConst.LOGIN_MEMBER, loginMember);

     //redirectURL 적용
     return "redirect:" + redirectURL;
}