使用@ControllerAdvice使简单的servlet过滤器工作

Opa*_*pal 11 spring exception-handling filter spring-security spring-boot

我有一个简单的过滤器,只是为了检查一个请求是否包含一个带有静态密钥的特殊标头 - 没有用户身份验证 - 只是为了保护端点.我的想法是抛出一个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() {}
}
Run Code Online (Sandbox Code Playgroud)

AccessForbiddenException

public class AccessForbiddenException extends RuntimeException {
  AccessForbiddenException(String message) {
    super(message)
  }
}
Run Code Online (Sandbox Code Playgroud)

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)
  }
}
Run Code Online (Sandbox Code Playgroud)

哪里我错了?简单的servlet过滤器可以使用spring-boot的异常映射吗?

M. *_*num 6

正如java servlet规范所指定的那样,Filter在Servlet调用a之前总是执行.现在a @ControllerAdvice只对在内部执行的控制器有用DispatcherServlet.因此,使用a Filter和期望a @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;
    }

}
Run Code Online (Sandbox Code Playgroud)


小智 5

您不能使用@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() {
    }
}
Run Code Online (Sandbox Code Playgroud)