복붙노트

[SPRING] @ControllerAdvice로 간단한 서블릿 필터 작동

SPRING

@ControllerAdvice로 간단한 서블릿 필터 작동

요청에 정적 키 (사용자 인증 없음)가있는 특수 헤더가 엔드 포인트를 보호하기 위해 포함되어 있는지 확인하는 간단한 필터가 있습니다. 아이디어는 키가 일치하지 않으면 AccessForbiddenException을 던지고 @ControllerAdvice로 주석이 달린 클래스의 응답에 매핑됩니다. 그러나 나는 그것을 작동시킬 수 없다. 내 @ExceptionHandler가 호출되지 않았습니다.

ClientKeyFilter

import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Controller

import javax.servlet.*
import javax.servlet.http.HttpServletRequest

@Controller //I know that @Component might be here
public class ClientKeyFilter implements Filter {

  @Value('${CLIENT_KEY}')
  String clientKey

  public void init(FilterConfig filterConfig) {}

  public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) {
    req = (HttpServletRequest) req
    def reqClientKey = req.getHeader('Client-Key')
    if (!clientKey.equals(reqClientKey)) {
      throw new AccessForbiddenException('Invalid API key')
    }
    chain.doFilter(req, res)
  }

  public void destroy() {}
}

액세스 금지

public class AccessForbiddenException extends RuntimeException {
  AccessForbiddenException(String message) {
    super(message)
  }
}

ExceptionController

@ControllerAdvice
class ExceptionController {
  static final Logger logger = LoggerFactory.getLogger(ExceptionController)

  @ExceptionHandler(AccessForbiddenException)
  public ResponseEntity handleException(HttpServletRequest request, AccessForbiddenException e) {
    logger.error('Caught exception.', e)
    return new ResponseEntity<>(e.getMessage(), I_AM_A_TEAPOT)
  }
}

내가 틀렸어? 간단한 서블릿 필터가 스프링 부트의 예외 매핑과 함께 작동 할 수 있습니까?

해결법

  1. ==============================

    1.java 서블릿 스펙에 지정된대로 서블릿이 호출되기 전에 항상 필터가 실행됩니다. 이제 @ControllerAdvice는 DispatcherServlet 내에서 실행되는 컨트롤러에만 유용합니다. 따라서 필터를 사용하고 @ControllerAdvice 또는이 경우 @ExceptionHandler가 호출 될 것으로 예상됩니다.

    java 서블릿 스펙에 지정된대로 서블릿이 호출되기 전에 항상 필터가 실행됩니다. 이제 @ControllerAdvice는 DispatcherServlet 내에서 실행되는 컨트롤러에만 유용합니다. 따라서 필터를 사용하고 @ControllerAdvice 또는이 경우 @ExceptionHandler가 호출 될 것으로 예상됩니다.

    JSON 응답을 작성하기 위해 필터에 동일한 논리를 입력하거나 필터 대신이 검사를 수행하는 HandlerInterceptor를 사용해야합니다. 가장 쉬운 방법은 HandlerInterceptorAdapter를 확장하고 preHandle 메서드를 재정의하고 구현 한 다음 필터의 논리를 해당 메서드에 넣는 것입니다.

    public class ClientKeyInterceptor extends HandlerInterceptorAdapter {
    
        @Value('${CLIENT_KEY}')
        String clientKey
    
        @Override
        public boolean preHandle(ServletRequest req, ServletResponse res, Object handler) {
            String reqClientKey = req.getHeader('Client-Key')
            if (!clientKey.equals(reqClientKey)) {
              throw new AccessForbiddenException('Invalid API key')
            }
            return true;
        }
    
    }
    
  2. ==============================

    2.@ControllerAdvice는 일부 컨트롤러에서 예외가 발생하면 호출되기 때문에 사용할 수 없지만 ClientKeyFilter는 @Controller가 아닙니다.

    @ControllerAdvice는 일부 컨트롤러에서 예외가 발생하면 호출되기 때문에 사용할 수 없지만 ClientKeyFilter는 @Controller가 아닙니다.

    @Controller 주석을 @Component로 바꾸고 다음과 같이 응답 본문과 상태를 설정해야합니다.

    @Component
    public class ClientKeyFilter implements Filter {
    
        @Value('${CLIENT_KEY}')
        String clientKey
    
        public void init(FilterConfig filterConfig) {
        }
    
        public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
            HttpServletRequest request = (HttpServletRequest) req;
            HttpServletResponse response = (HttpServletResponse) res;
    
            String reqClientKey = request.getHeader("Client-Key");
    
            if (!clientKey.equals(reqClientKey)) {
                response.sendError(HttpServletResponse.SC_FORBIDDEN, "Invalid API key");
                return;
            }
    
            chain.doFilter(req, res);
        }
    
        public void destroy() {
        }
    }
    
  3. from https://stackoverflow.com/questions/30335157/make-simple-servlet-filter-work-with-controlleradvice by cc-by-sa and MIT license