6

我有一个简单的过滤器来检查请求是否包含带有静态键的特殊标头 - 没有用户身份验证 - 仅用于保护端点。这个想法是如果密钥不匹配,则会抛出AccessForbiddenException,然后将映射到带有注解为@ControllerAdvice的类的响应。然而,我无法让它工作。我的@ExceptionHandler未被调用。使用@ControllerAdvice创建简单的servlet过滤器

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() {} 
} 

AccessForbiddenException

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) 
    } 
} 

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

+0

这绝不会发生过滤器。 '@ ControllerAdvice'只对请求到达'DispatcherServlet'有用,'Filter's总是在那之前执行。要么把这个逻辑放在过滤器中,要么代替过滤器使用'HandlerInterceptor'。 –

+0

@ M.Deinum,我终于使用了'HandlerInterceptor'。如果您想将其添加为答案,我会很乐意接受它。 – Opal

回答

3

正如Java Servlet规范调用一个ServletFilter总是执行规定。现在,@ControllerAdvice仅适用于在DispatcherServlet内部执行的控制器。因此使用Filter并且期望@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; 
    } 

} 
+0

可能是因为您发布了您的答案而发生了变化,但现在的签名是: '公共布尔preHandle(HttpServletRequest请求,HttpServletResponse响应,对象处理程序)抛出异常' – Zakaria

4

您不能使用@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() { 
    } 
}