Filter 사용 예제

YH·2023년 3월 1일
0

Filter 사용 예제

  1. 모든 요청에 대한 로그를 남기는 Filter
@Slf4j
public class LogFilter 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 {
        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");
        Filter.super.destroy();
    }
}
  • 필터를 사용하기 위해 필터 인터페이스를 위와 같이 구현
  • doFilter에 ServletRequest는 HTTP 요청이 아닌 경우까지 고려해서 만든 인터페이스로 HTTP를 사용하면 아래와 같이 Down Casting하면 됨

    HttpServletRequest httpRequest = (HttpServletRequest) request;

  • 각 HTTP 요청을 구분하기 위해 UUID 사용
  • 아래 로직을 반드시 호출해야 다음 필터나 서블릿을 호출 함, 해당 로직이 없으면 서블릿이나 컨트롤러까지 호출되지 않고 종료 됨

    chain.doFilter(request, response);

1-1. Filter를 사용하기 위한 설정

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

        return filterRegistrationBean;
    }
}
  • 스프링 부트의 경우, FilterRegistrationBean을 사용하여 Filter를 등록 함
  • setFilter() : 사용할 Filter를 지정
  • setOrder() : Filter의 순서를 지정, 숫자가 낮을수록 먼저 동작 함
  • addUrlPatterns() : 필터를 적용할 URL 패턴을 지정, 한 번에 여러 패턴 지정 가능
  1. 인증 체크 Filter
@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);
	}
}
  • 미 인증 사용자는 로그인 화면으로 rediect 함, redirect할 때 로그인 후 원래 요청했던 화면으로 이동해주도록 redirect URL을 지정해준다.

    httpResponse.sendRedirect("/login?redirectURL=" + requestURI);

2-1. 인증 체크를 사용하기 위한 필터 설정

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

2-2. 로그인 후, 요청했던 화면으로 이동하도록 컨트롤러 작성

  • LoginController 내에 로그인 메소드 로직
/**
* 로그인 이후 redirect 처리
*/
@PostMapping("/login")
public String loginV4(@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());
	log.info("login? {}", loginMember);
    
	if (loginMember == null) {
		bindingResult.reject("loginFail", "아이디 또는 비밀번호가 맞지 않습니다.");
		return "login/loginForm";
	}
    
    //로그인 성공 처리
    //세션이 있으면 있는 세션 반환, 없으면 신규 세션 생성
    HttpSession session = request.getSession();
    //세션에 로그인 회원 정보 보관
    session.setAttribute(SessionConst.LOGIN_MEMBER, loginMember);
    
    //redirectURL 적용
    return "redirect:" + redirectURL;
}
  • Filter는 chaing.doFilter(request, response)에서 request 및 response를 다른 객체로 바꿀 수 있다, Interceptor는 불가능 함
  • SevletRequest, ServletResponse를 구현한 다른 객체를 넘겨줄 수 있음
  • 잘 사용되는 기능은 아니니 참고만 하자.
profile
하루하루 꾸준히 포기하지 말고

0개의 댓글