본문 바로가기
개발 이론/Spring Security

[Spring Security] 필터로 처리??인터셉터로 처리??

by dal_been 2023. 11. 16.
728x90

스프링 시큐리티 공부할때도 내가 아는 서블릿 필터가 시큐리티가 사용하는 필터가 맞나?? 라는 생각이 들었다. 그래서

일단 시큐리티의 필터도 서블릿 필터의 일종이다. 근데 필터가 뭐였지..?에서 시작해서 인터셉터라는 개념도 있던거 같은데...

하면서 공부해보니 의문이 들었다. 왜 스프링 시큐리티 필터로 로그인 처리를 할까?? 인터셉터도 있는데...??

 

결론부터 말하자면 처리를 하는 위치??가 다르다. 사실 인터셉터로 구현하든, 스프링 시큐리티로 구현하든 그건 회사의 분위기나 개발자들의 선호도에 따라 다르다고 한다. 뭐가 나쁘다,좋다라는 그런건 없는 것같다.

 

그래서 일단 필터와 인터셉터에 대해서 간단하게 얘기해보자

 

 

  필터

 

필터의 흐름은

Http 요청 -> was -> 필터 -> 서블릿 -> 컨트롤러  이다.

즉 서블릿으로 흐름이 이어지기전에 필터가 실행된다

 

필터를 구현하고 싶다면 밑의 필터 인터페이시를 구현하면 된다

  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를 구현한 클래스를 FilterRegistrationBean을 이용해서 등록해주면 된다

 

 

인터셉터

 

스프링 mvc가 제공하는 기술로 디스패처 서블릿과 컨트롤러 사이에서 호출된다

 

Http 요청 -> was -> 필터 -> 서블릿 -> 스프링 인터셉터 -> 컨트롤러

 

여기서 일단 필터와 다른 점은 실행되는 위치와 편리함+정교함이다.

필터같은 경우 서블릿에 들어가기전 실행되고 doFilter()하나만 제공하고 request,response만 제공하지만 

인터셉터의 경우 컨트롤러 호출전, 컨트롤러 호출후, 요청완료이후 단계적으로 세부화되어있고 어떤 컨트롤러가 호출되는 지 호출정보를 알 수 있다.

public interface HandlerInterceptor {
    default boolean preHandle(HttpServletRequest request, 
    		HttpServletResponse response,Object handler) throws Exception {}
    
    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 {}
}

 

만약 컨트롤러에서 예외가 발생하면 postHandle은 호출되지 않지만 afterCompletion은 항상 호출된다

 

인터셉테를 구현하고 싶다면 위의 인터페이스르 구현하고

WebMvcConfigurer를 구현한 클래스에서 addInterceptors를 오버라이드해줘서 어떤 url에 적용할지 말지, 순서는 어떻게 할지 정해주면된다

 


간단하게 필터와 인터셉터에 대해서 얘기해보았다. 완전 자세하게는 아니지만 어느정도 차이를 인지했을 것이다.

이제 인터셉터로 스프링 시큐리티의 역할??을 구현해보자. 

 

인터셉티로 스프링 시큐리티처럼 구현해보기

 

일단 어노테이션과 HandlerMethodArgumentResolver를 구현한 클래스가 있으면 된다

 

  @Target(ElementType.PARAMETER)
  @Retention(RetentionPolicy.RUNTIME)
  public @interface Login {
  }

 

@Bean
 public class LoginMemberArgumentResolver implements HandlerMethodArgumentResolver {
      @Override
      public boolean supportsParameter(MethodParameter parameter) { 
          log.info("supportsParameter 실행");
           boolean hasLoginAnnotation = parameter.hasParameterAnnotation(Login.class);
           boolean hasMemberType = Member.class.isAssignableFrom(parameter.getParameterType());
           return hasLoginAnnotation && hasMemberType;
      }
      
	  @Override
      public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
            log.info("resolveArgument 실행");
            HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();
            HttpSession session = request.getSession(false);
            if (session == null) {
                   return null;
            }
            
           return session.getAttribute(SessionConst.LOGIN_MEMBER);
      }
}

 
사실 LoginMemberArgumentResolver에서 TokeProvider를 주입받아서 스프링 시큐리티의 토큰 검증 처럼 구현할 수 있다. 
여기서는 예제를 단순하게 하기위해서 세션에서 값을 가져와서 검사하였다.

 

 


근데 여기서 또 찾다보니.... 스프링 시큐리티 예외는 @ControllerAdvice에서 처리할 수 없다는...이야기를 보았다..

 

왜..???

생각해보면 일반적으로 예외란 애플리케이션 컨트롤러가 요청을 받은후 처리과정에 발생한다.

그렇게 생각해보면 스프링 시큐리티의 필터는 컨트롤러에 도달하기전, 아니 서블릿에 도달하기전에 필터체인에서 예외를 발생시킨다.

그래서 @ControllerAdvice는 컨트롤러 계층에서 발생하는 예외를 처리하기 때문에 처리할 수 없는 것이다.

 

 

잉...그럼 해결방법 없어..??? 예외를 한곳에서 처리하고 싶은데...

있다! AuthenticationEntryPoint에서 예외처리를 시도하는 것이다.

 

@Component
public class TestEntryPoint implements AuthenticationEntryPoint{

	@Autowired
    @Qualifier("handlerResolver")
    private HandlerExceptionResolver resolver;
    
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) 
      throws IOException, ServletException {
        resolver.resolveException(request, response, null, authException);
    }
}
@ControllerAdvice
public class DefaultExceptionHandler extends ResponseEntityExceptionHandler {

    @ExceptionHandler({ AuthenticationException.class })
    @ResponseBody
    public ResponseEntity<RestError> handleAuthenticationException(Exception ex) {

        RestError re = new RestError(HttpStatus.UNAUTHORIZED.toString(), 
          "Authentication failed at controller advice");
        return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(re);
    }
}

 

 

 

 


https://www.baeldung.com/spring-security-exceptionhandler

https://colabear754.tistory.com/172#recentEntries