프로젝트[종료]

14. Servlet Filter 적용하기

알렉스 페레이라 2023. 5. 4. 20:21

저번에는 사용자가 로그인 한 다음에 특정 값을 Session에 저장하여 로그인상태를 유지했다.

지금부터는 Servlet이 제공하는 Filter, Spring이 제공하는 Interceptor에 대해서 알아보고, 적용해보겠다.

 

 

Filter

  • Servlet이 제공하는 기능으로, WAS와 Servlet사이에서 공통관심사를 처리하기 위한 기능이다.
  • Chain으로 구성되며, 중간에 필터를 자유롭게 추가할 수 있다.
  • 필터 인터페이스를 구현하고 등록하면 서블릿 컨테이너가 필터를 싱글톤 객체로 생성하고, 관리한다
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(): 고객의 요청이 올 때 마다 해당 메서드가 호출된다. 필터의 로직을 구현하면 된다.
  • destroy(): 필터 종료 메서드, 서블릿 컨테이너가 종료될 때 호출된다.

 

Filter의 동작 흐름

 

1.SessionFilter.java를 생성한다.

경로(\src\main\java\com\project\web\session\SessionFilter.java)

package com.project.web.session;

import com.project.domain.session.SessionConst;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import org.springframework.util.PatternMatchUtils;

import java.io.IOException;

public class SessionFilter implements Filter {

    //SessionFilter에서 예외되는 Path들이다.
    private static final String[] excludePath = {"/", "/sign/*", "/assets/*", "/images/*"};
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        String requestURI = httpRequest.getRequestURI(); //요청한 URI, SessionFilter이후에 redirect시키기위해 변수선언.

        try {
            //SessionFilter 예외처리 되지 않은 Path라면
            if (!isExclude(requestURI)) {
                //세션을 불러온다.
                HttpSession session = httpRequest.getSession();
                //세션이 null이거나, 세션에 로그인정보가 없다면 로그인화면으로 redirect시킨다.
                if (session == null || session.getAttribute(SessionConst.SESSION_NAME) == null) {
                    httpResponse.sendRedirect("/sign/signIn?redirectUrl=" + requestURI);
                    return;
                }
            }
            //다음 필터를 실행하거나, Servlet, Controller를 호출한다.
            chain.doFilter(request, response);
        } catch (Exception e) {
            throw e;
        }
    }

    protected boolean isExclude(String requestURI) {
        return PatternMatchUtils.simpleMatch(excludePath, requestURI);
    }
}

 

2.@Configuration 클래스를 작성하여, SessionFilter를 Spring Bean으로 등록한다.

경로(\src\main\java\com\project\config\WebConfig.java)

 

package com.project.config;

import com.project.web.session.SessionFilter;
import jakarta.servlet.Filter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class WebConfig {
    @Bean
    public FilterRegistrationBean sessionFilter() {
        FilterRegistrationBean<Filter> sessionFilterRegistrationBean = new FilterRegistrationBean<Filter>();
        sessionFilterRegistrationBean.setFilter(new SessionFilter()); //SessionFilter 객체를 등록한다.
        sessionFilterRegistrationBean.setOrder(0); //Filter의 순서를 정한다. 순서가 작을수록 chain에 먼저 걸림
        sessionFilterRegistrationBean.addUrlPatterns("/*"); //해당 필터가 적용될 URL 패턴을 지정한다.

        return sessionFilterRegistrationBean;
    }
}

 

3.SignController에서, SessionFilter에 걸러진 사용자가 정상적으로 로그인 했을때 요청한 URI로 다시 redirect한다.

@RequestParam을 사용하여 해당 Parameter를 Mapping한다.

    @PostMapping("/signIn")
    public String addSignIn(@Validated @ModelAttribute SignInForm signInForm, BindingResult bindingResult, HttpServletRequest request, @RequestParam(defaultValue = "/") String redirectUrl) {
        if (bindingResult.hasErrors()) {
            return "/login/signIn";
        }

        User user = signService.findLoginUser(signInForm.getLoginId(), signInForm.getPassword());
        if (user == null) {
            bindingResult.addError(new ObjectError("signInForm", "아이디나 비밀번호가 틀렸습니다."));
            return "/login/signIn";
        }else{
            //로그인 성공시 세션처리
            HttpSession httpSession = request.getSession();
            httpSession.setAttribute(SessionConst.SESSION_NAME, user);
        }
        return "redirect:" + redirectUrl;
    }

 

4.서버 재시작후 테스트

로그인하지 않은 상태에서 localhost:9090/generic으로 접근한다.

로그인 페이지로 redirect됐으며, 접근 시도했던 /generic경로를 redirectURL파라미터로 던진다.

 

5.로그인

성공!

 

다음에는 Spring이 제공하는 Interceptor를 사용해 볼 예정이다.