개발일기장

[사이드 프로젝트 - xuni] 세션 + (필터, 인터셉터)로 인증 로직 구현하고 테스트 환경 만들기

자몽포도 2023. 4. 4. 18:53

최종적으로 jwt를 사용하게 될텐데 세션을 한 번 사용하고 건너가려고합니다.

 

목차

1. 세션 

2. 필터

3.인터셉터

4. 테스트 환경에서 인증 로직 처리하기(필터/인터셉터)


 

1. 세션

세션 인증을 구현하는 방법은 다양하겠지만 저는 HttpSession 을 주입받아 사용했습니다. 인증 정보가 담긴 세션은 로그인 과정에서 저장됩니다.

로그인 로직을 통과할 경우 Session에 memberDetails 객체를 저장합니다. memberDetails  객체는 유저 ID, 이메일 등의 정보를 담고 있습니다.

 

이후 사용자의 인증 여부가 필요한 앤드포인트마다 인증 여부를 확인해야 하는데요. 매우 번거로운 작업이기 때문에 필터 혹은 인터셉터를 통해 구현합니다.

 

세션을 이용해서 아래와 같이 DB에 접근하지 않고 유저에 접근할 수도 있습니다.


2. 필터

필터와 인터셉터의 차이점 중 하나는 동작하는 시점입니다. 스프링 부트 환경에서 애플리케이션을 실행할 경우 필터는 디스패처 서블릿 전에 doFilter() 메서드가 동작합니다. 인터셉터는 핸들러 어뎁터 전에 preHandle() 가 동작합니다.

 

(대충 그림)

 

필터를 구현하기 위해서는 servlet.filter 인터페이스를 구현하면 됩니다.

public interface Filter {
    default void init(FilterConfig filterConfig) throws ServletException {}

    void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException;

    default void destroy() {}
}

init, destory 는 디폴트 메서드라 따로 구현을 하지 않아도 된다. doFilter() 메서드에 필터 처리할 로직을 작성하고 chain.doFilter()를 사용해서 다음 필터 혹은 디스패처 서블릿으로 넘어갈 수 있도록 해야합니다. 

 

 

로그인, 로그아웃, h2 콘솔에 접근하는 URI에는 인증 대상에서 제외하고 나머지는 loginMember 라는 세션이 있는지 확인하고 없다면 미인증 사용자로 간주하고 예외를 발생시킵니다. 필터는 서블릿 기술 그리고 스프링의 관리를 받기 시작하는 디스패처서블릿 전에 호출되기 때문에 @ControllerAdvice 로 예외처리를 하지 못한다는 아쉬운 점이 있다.

 


3.인터셉터

인터셉터는 컨트롤러 전에 실행되는 핸들러 어뎁터 직전에  preHandle()이 시작되고 이후 컨트롤러에서 디스패처 서블릿으로 돌아올 때 postHandle() 이 동작하고 클라이언트의 요청이 아예 끝났을 때 afterCompletion을 호출한다. 

public interface HandlerInterceptor {
   default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
         throws Exception {
      return true;
   }
   
   default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
         @Nullable ModelAndView modelAndView) throws Exception {
   }

   default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
         @Nullable Exception ex) throws Exception {
   }

}

 

저는 인증 인터셉터 구현에서 필요한 preHandle()만 오버라이딩 하였습니다.

로직 자체는 필터에서 ServletRequest 를 HttpServletRequest로 다운 캐스팅을 제외하면 크게 다를게 없습니다. 한 가지 다른 점은 인터셉터 적용 엔드포인트입니다. 인터셉터를 적용할 엔드포인트는 아래와 같이 설정 파일에서 적용합니다.

 


4. 테스트 환경에서 인증 로직 처리하기(필터/인터셉터)

인증 로직이 추가될 경우 컨트롤러 단 테스트를 할 때 인증 로직이 동작할 확률이 매우 높습니다. 하지만 인증에는 관심이 없을 때가 있습니다. 이럴 때는 어떡해면 좋을까요? 방법이야 다양하겠지만 저는 깡통 Session을 주입하는 방식으로 문제를 해결했습니다.

 

필터

필터는 빈으로 등록했기 때문에 테스트 환경에서 인증 필터 빈을 대체하면 됩니다. 먼저 LoginFilter 를 상속해서 테스트 전용 인증 필터를 만들어줍시다.

 

이후 @TestConfiguration을 통해 LoginFilter의 구현체를 TestLoginFilter로 등록하면 됩니다.

 

인터셉터

인터셉터는 테스트 환경에서만 인터셉터를 하나 더 등록하도록 만들었습니다. 이 인터셉터는 인증 인터셉터보다 먼저 동작해서 깡통 Session을 주입합니다. 굳이 AuthInterceptor를 상속할 이유는 없지만 헷갈리지 않으려고 상속받았습니다.

 

order(1) 를 통해 테스트 인터셉터가 먼저 동작하도록 만듭니다. 물론 인증 인터셉터에 order(2) 이런식으로 번호를 지정해야 테스트 인터셉터 이후에 동작합니다.