본문 바로가기

스프링/Spring MVC

서블릿 필터 사용해 인증 체크하기

로그인되지 않은 사용자는 로그인이 필요한 여러 페이지에 접근을 하지 못해야 합니다.

여러 로직들은 인증이라는 공통 관심사를 가지고 있고 서블릿 필터를 사용해 해당 인증 기능을 공통적으로 적용하는 방법을 알아보겠습니다.

 

인증 체크 필터 만들기

@Slf4j
public class LoginCheckFilter implements Filter {
    // 인증과 무관하게 항상 접근을 허용하는 요청 Url
    private static final String[] whitelist = {"/", "/members/add", "/login",
            "/logout", "/css/*"};

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
        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 (IOException e) {
            e.printStackTrace();
        } catch (ServletException e) {
            e.printStackTrace();
        }
    }

    // 화이트 리스트의 경우 인증 체크 x
    private boolean isLoginCheckPath(String requestURI) {
        return !PatternMatchUtils.simpleMatch(whitelist, requestURI);
    }
}
  • whitelist = {"/", "/members/add", "/login", "/logout","/css/*"};
    • 인증 필터를 적용해도 홈, 회원가입, 로그인 화면, css 같은 리소스에는 접근할 수 있어야 합니다. 이렇게 화이트 리스트 경로는 인증과 무관하게 항상 허용합니다. 화이트 리스트를 제외한 나머지 모든 경로에는 인증 체크 로직을 적용합니다.
  • isLoginCheckPath(requestURI)
    • 화이트 리스트를 제외한 모든 경우에 인증 체크 로직을 적용합니다.
  • httpResponse.sendRedirect("/login?redirectURL=" + requestURI);
    • 미인증 사용자는 로그인 화면으로 리다이렉트 합니다. 그런데 로그인 이후에 다시 홈으로 이동해버리면, 원하는 경로를 다시 찾아가야 하는 불편함이 있습니다. 예를 들어서 상품 관리 화면을 보려고 들어갔다가 로그인 화면으로 이동하면, 로그인 이후에 다시 상품 관리 화면으로 들어가는 것이 좋습니다. 현재 요청한 경로인 requestURI를 /login에 쿼리 파라미터로 함께 전달합니다.
  • return; 필터를 더는 진행하지 않습니다. 이후 필터는 물론 서블릿, 컨트롤러가 더는 호출되지 않습니다. 앞서 redirect 를 사용했기 때문에 redirect가 응답으로 적용되고 요청이 끝납니다.

 

WebConfig - loginCheckFilter() 추가

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Bean
    public FilterRegistrationBean loginCheckFilter(){
        FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>();
        filterRegistrationBean.setFilter(new LoginCheckFilter());
        filterRegistrationBean.setOrder(2);
        filterRegistrationBean.addUrlPatterns("/*");
        return filterRegistrationBean;
    }
}
  • setFilter(new LoginCheckFilter()) : 로그인 필터를 등록합니다.
  • setOrder(2) : 순서를 2번으로 잡았다. 로그 필터 다음에 로그인 필터가 적용됩니다.
  • addUrlPatterns("/*") : 모든 요청에 로그인 필터를 적용합니다.

 

RedirectURL 처리

@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());
        log.info("login?{}", loginMember);

        if (loginMember == null){
            bindingResult.reject("loginFali", "아이디 또는 비번이 맞지 않아~~");
            return "login/loginForm";
        }

        // 로그인 성공 처리
        // 세션 관리자를 통해 세션을 생성하고, 회원 데이터를 보관
        HttpSession session = request.getSession();
        session.setAttribute(SessionConst.LOGIN_MEMBER, loginMember);
        return "redirect:"+redirectURL;
}

로그인 체크 필터에서, 미인증 사용자는 요청 경로를 포함해서 /login redirectURL 요청 파라미터를 추가해서 요청합니다. 이 값을 사용해서 로그인 성공 시 해당 경로로 고객을 redirect 합니다.

 

서블릿 필터를 사용한 덕분에 로그인하지 않은 사용자는 나머지 경로에 들어갈 수 없게 되었습니다. 공통 관심사를 서블릿 필터를 사용해서 해결한 덕분에 향후 로그인 관련 정책이 변경되어도 이 부분만 변경하면 됩니다.