如何在Spring中拦截RequestRejectedException?

我在Tomcat日志中看到了大量RequestRejectedException条目(下面粘贴了样本)。 几个月前,经过次要版本升级(Spring Security 4.2.4,IIRC)之后,这些内容开始出现在我的日志文件中,因此这显然是Spring中默认启用的新安全function。 这里报告了一个类似的问题,但我的问题具体涉及如何在控制器中拦截这些exception。 针对此问题记录了一个Spring Security错误( 提供一种处理RequestRejectedException的方法 )。 但是,在Spring 5.1之前,他们并未针对此问题进行修复。

我理解为什么抛出这些exception ,我不想禁用此安全function 。

我想获得对此function的一些控制,以便:

  1. 我知道我不会阻止合法用户访问我的网站。
  2. 我可以看到哪些请求触发了这个(它们是SQL注入攻击吗?)
  3. 我可以调整服务器响应。 Spring Security防火墙将完整的堆栈跟踪转储到Web客户端(信息泄露),以及500 Internal Server Error (这非常不正确,这应该是400 Bad Request )。

我想找到一种方法来记录所请求的URL,但也可以专门针对这些exception来抑制堆栈跟踪,因为它们会在不给我任何有用信息的情况下污染我的日志文件。 最理想的情况是,我想截取这些exception并在我的应用程序层中处理它们,而不是在Tomcat日志中报告它们。

例如,这是我的catalina.out每天出现的数千个日志条目之一:

 Aug 10, 2018 2:01:36 PM org.apache.catalina.core.StandardWrapperValve invoke SEVERE: Servlet.service() for servlet [dispatcher] in context with path [] threw exception org.springframework.security.web.firewall.RequestRejectedException: The request was rejected because the URL contained a potentially malicious String ";" at org.springframework.security.web.firewall.StrictHttpFirewall.rejectedBlacklistedUrls(StrictHttpFirewall.java:265) at org.springframework.security.web.firewall.StrictHttpFirewall.getFirewalledRequest(StrictHttpFirewall.java:245) at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:193) at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:177) at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:347) at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:263) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:496) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342) at org.apache.coyote.ajp.AjpProcessor.service(AjpProcessor.java:486) at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:790) at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1459) at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) at java.lang.Thread.run(Thread.java:748) 

我在两天内看到了超过3,200个,并且它很快成为我的catalina.out日志文件的最大贡献者,以至于它阻止我看到其他合法的问题。 从本质上讲,这个新的Spring Securityfunction是内置拒绝服务的一种forms,自4月以来浪费了我几个小时的时间。 我并不是说这不是一个重要的function,只是默认的实现是完全拙劣的,我想找到一种方法来获得对它的控制,无论是作为开发人员还是作为系统管理员。

我使用自定义错误控制器来拦截Spring中的许多其他exception类型(包括IOException )。 但是, RequestRejectedException似乎因某种原因而失败。

这是我的ErrorController.java的相关部分,以便了解我正在尝试完成的任务:

 @ControllerAdvice public final class ErrorController { /** * Logger. */ private static final Logger LOGGER = Logger.getLogger(ErrorController.class.getName()); /** * Generates an Error page by intercepting exceptions generated from HttpFirewall. * * @param ex A RequestRejectedException exception. * @return The tile definition name for the page. */ @ExceptionHandler(RequestRejectedException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) public String handleRequestRejectedException(final HttpServletRequest request, final RequestRejectedException ex) { if (LOGGER.isLoggable(Level.INFO)) { LOGGER.log(Level.INFO, "Request Rejected", ex); } LOGGER.log(Level.WARNING, "Rejected request for [" + request.getRequestURL().toString() + "]. Reason: " + ex.getMessage()); return "errorPage"; } /** * Generates a Server Error page. * * @param ex An exception. * @return The tile definition name for the page. */ @ExceptionHandler(Exception.class) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) public String handleException(final Exception ex) { if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, "Server Error", ex); } return "errorPage"; } } 

此错误控制器适用于许多例外。 例如,它成功拦截了这个IllegalStateException

 Aug 05, 2018 7:50:30 AM com.mycompany.spring.controller.ErrorController handleException SEVERE: Server Error java.lang.IllegalStateException: Cannot create a session after the response has been committed at org.apache.catalina.connector.Request.doGetSession(Request.java:2999) ... 

但是,这不会拦截RequestRejectedException (由上面第一个日志示例中缺少“服务器错误”指示)。

如何在错误控制器中拦截RequestRejectedException

我实现了StrictHttpFirewall的子类, StrictHttpFirewall请求信息记录到控制台,并抛出一个带有抑制堆栈跟踪的新exception。 这部分解决了我的问题(至少我现在可以看到错误的请求)。

如果您只是想在没有堆栈跟踪的情况下查看被拒绝的请求,那么这就是您正在寻找的答案。

如果要在控制器中处理这些exception,请参阅已接受的答案以获得完整(但稍微复杂一点)的解决方案。


LoggingHttpFirewall.java

此类扩展StrictHttpFirewall以捕获RequestRejectedException并使用来自请求的元数据和抑制的堆栈跟踪引发新的exception。

 import java.util.logging.Level; import java.util.logging.Logger; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.security.web.firewall.FirewalledRequest; import org.springframework.security.web.firewall.RequestRejectedException; import org.springframework.security.web.firewall.StrictHttpFirewall; /** * Overrides the StrictHttpFirewall to log some useful information about blocked requests. */ public final class LoggingHttpFirewall extends StrictHttpFirewall { /** * Logger. */ private static final Logger LOGGER = Logger.getLogger(LoggingHttpFirewall.class.getName()); /** * Default constructor. */ public LoggingHttpFirewall() { super(); return; } /** * Provides the request object which will be passed through the filter chain. * * @returns A FirewalledRequest (required by the HttpFirewall interface) which * inconveniently breaks the general contract of ServletFilter because * we can't upcast this to an HttpServletRequest. This prevents us * from re-wrapping this using an HttpServletRequestWrapper. * @throws RequestRejectedException if the request should be rejected immediately. */ @Override public FirewalledRequest getFirewalledRequest(final HttpServletRequest request) throws RequestRejectedException { try { return super.getFirewalledRequest(request); } catch (RequestRejectedException ex) { if (LOGGER.isLoggable(Level.WARNING)) { LOGGER.log(Level.WARNING, "Intercepted RequestBlockedException: Remote Host: " + request.getRemoteHost() + " User Agent: " + request.getHeader("User-Agent") + " Request URL: " + request.getRequestURL().toString()); } // Wrap in a new RequestRejectedException with request metadata and a shallower stack trace. throw new RequestRejectedException(ex.getMessage() + ".\n Remote Host: " + request.getRemoteHost() + "\n User Agent: " + request.getHeader("User-Agent") + "\n Request URL: " + request.getRequestURL().toString()) { private static final long serialVersionUID = 1L; @Override public synchronized Throwable fillInStackTrace() { return this; // suppress the stack trace. } }; } } /** * Provides the response which will be passed through the filter chain. * This method isn't extensible because the request may already be committed. * Furthermore, this is only invoked for requests that were not blocked, so we can't * control the status or response for blocked requests here. * * @param response The original HttpServletResponse. * @return the original response or a replacement/wrapper. */ @Override public HttpServletResponse getFirewalledResponse(final HttpServletResponse response) { // Note: The FirewalledResponse class is not accessible outside the package. return super.getFirewalledResponse(response); } } 

WebSecurityConfig.java

WebSecurityConfig ,将HTTP防火墙设置为LoggingHttpFirewall

 @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { /** * Default constructor. */ public WebSecurityConfig() { super(); return; } @Override public final void configure(final WebSecurity web) throws Exception { super.configure(web); web.httpFirewall(new LoggingHttpFirewall()); // Set the custom firewall. return; } } 

结果

在将此解决方案部署到生产环境后,我很快发现StrictHttpFirewall的默认行为阻止Google索引我的网站!

 Aug 13, 2018 1:48:56 PM com.mycompany.spring.security.AnnotatingHttpFirewall getFirewalledRequest WARNING: Intercepted RequestBlockedException: Remote Host: 66.249.64.223 User Agent: Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.96 Mobile Safari/537.36 (compatible; Googlebot/2.1; +http://www.google.com/bot.html) Request URL: https://www.mycompany.com/10.1601/tx.3784;jsessionid=692804549F9AB55F45DBD0AFE2A97FFD 

我一发现这个,就迅速部署了一个新版本(包括在我的其他答案中 ),寻找;jsessionid=并允许这些请求通过。 可能还有其他请求也应该通过,现在我有办法检测这些请求。

事实certificate,虽然HttpFirewallStrictHttpFirewall包含几个设计错误(在下面的代码中记录),但几乎不可能逃脱Spring Security的One True Firewall并通过请求属性将HttpFirewall信息传递给可以传递这些标记请求的HandlerInterceptor到一个真正的 (持久的)防火墙,而不会牺牲首先标记它们的原始业务逻辑。 这里记录的方法应该是相当面向未来的,因为它符合来自HttpFirewall接口的简单契约,其余的只是核心Spring Framework和Java Servlet API。

对于我之前的回答,这实际上是一个更复杂但更完整的替代方案。 在这个答案中,我实现了StrictHttpFirewall一个新子类,它在特定的日志记录级别拦截并记录被拒绝的请求,但也为HTTP请求添加了一个属性,该属性标记它以供下游filter(或控制器)处理。 此外,此AnnotatingHttpFirewall提供了一个inspect()方法,该方法允许子类为阻止请求添加自定义规则。

这个解决方案分为两部分:(1) Spring Security和(2) Spring Framework(Core) ,因为这是导致这个问题的鸿沟,这显示了如何桥接它。

作为参考,这是在Spring 4.3.17和Spring Security 4.2.6上测试的。 Spring 5.1发布时可能会发生重大变化。


第1部分:Spring Security

这是在Spring Security中执行日志记录和标记的解决方案的一半。


AnnotatingHttpFirewall.java

 import java.util.logging.Level; import java.util.logging.Logger; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.security.web.firewall.FirewalledRequest; import org.springframework.security.web.firewall.RequestRejectedException; import org.springframework.security.web.firewall.StrictHttpFirewall; /** * Overrides the StrictHttpFirewall to log some useful information about blocked requests. */ public class AnnotatingHttpFirewall extends StrictHttpFirewall { /** * The name of the HTTP header representing a request that has been rejected by this firewall. */ public static final String HTTP_HEADER_REQUEST_REJECTED_FLAG = "X-HttpFirewall-RequestRejectedFlag"; /** * The name of the HTTP header representing the reason a request has been rejected by this firewall. */ public static final String HTTP_HEADER_REQUEST_REJECTED_REASON = "X-HttpFirewall-RequestRejectedReason"; /** * Logger. */ private static final Logger LOGGER = Logger.getLogger(AnnotatingHttpFirewall.class.getName()); /** * Default constructor. */ public AnnotatingHttpFirewall() { super(); return; } /** * Provides the request object which will be passed through the filter chain. * * @param request The original HttpServletRequest. * @returns A FirewalledRequest (required by the HttpFirewall interface) which * inconveniently breaks the general contract of ServletFilter because * we can't upcast this to an HttpServletRequest. This prevents us * from re-wrapping this using an HttpServletRequestWrapper. */ @Override public FirewalledRequest getFirewalledRequest(final HttpServletRequest request) { try { this.inspect(request); // Perform any additional checks that the naive "StrictHttpFirewall" misses. return super.getFirewalledRequest(request); } catch (RequestRejectedException ex) { final String requestUrl = request.getRequestURL().toString(); // Override some of the default behavior because some requests are // legitimate. if (requestUrl.contains(";jsessionid=")) { // Do not block non-cookie serialized sessions. Google's crawler does this often. } else { // Log anything that is blocked so we can find these in the catalina.out log. // This will give us any information we need to make // adjustments to these special cases and see potentially // malicious activity. if (LOGGER.isLoggable(Level.WARNING)) { LOGGER.log(Level.WARNING, "Intercepted RequestBlockedException: Remote Host: " + request.getRemoteHost() + " User Agent: " + request.getHeader("User-Agent") + " Request URL: " + request.getRequestURL().toString()); } // Mark this request as rejected. request.setAttribute(HTTP_HEADER_REQUEST_REJECTED, Boolean.TRUE); request.setAttribute(HTTP_HEADER_REQUEST_REJECTED_REASON, ex.getMessage()); } // Suppress the RequestBlockedException and pass the request through // with the additional attribute. return new FirewalledRequest(request) { @Override public void reset() { return; } }; } } /** * Provides the response which will be passed through the filter chain. * This method isn't extensible because the request may already be committed. * Furthermore, this is only invoked for requests that were not blocked, so we can't * control the status or response for blocked requests here. * * @param response The original HttpServletResponse. * @return the original response or a replacement/wrapper. */ @Override public HttpServletResponse getFirewalledResponse(final HttpServletResponse response) { // Note: The FirewalledResponse class is not accessible outside the package. return super.getFirewalledResponse(response); } /** * Perform any custom checks on the request. * This method may be overridden by a subclass in order to supplement or replace these tests. * * @param request The original HttpServletRequest. * @throws RequestRejectedException if the request should be rejected immediately. */ public void inspect(final HttpServletRequest request) throws RequestRejectedException { final String requestUri = request.getRequestURI(); // path without parameters // final String requestUrl = request.getRequestURL().toString(); // full path with parameters if (requestUri.endsWith("/wp-login.php")) { throw new RequestRejectedException("The request was rejected because it is a vulnerability scan."); } if (requestUri.endsWith(".php")) { throw new RequestRejectedException("The request was rejected because it is a likely vulnerability scan."); } return; // The request passed all custom tests. } } 

WebSecurityConfig.java

WebSecurityConfig ,将HTTP防火墙设置为AnnotatingHttpFirewall

 @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { /** * Default constructor. */ public WebSecurityConfig() { super(); return; } @Override public final void configure(final WebSecurity web) throws Exception { super.configure(web); web.httpFirewall(new AnnotatingHttpFirewall()); // Set the custom firewall. return; } } 

第2部分:Spring框架

可以想象,该解决方案的第二部分可以实现为ServletFilterHandlerInterceptor 。 我将使用HandlerInterceptor的路径,因为它似乎提供了最大的灵活性,并直接在Spring Framework中工作。


RequestBlockedException.java

此自定义exception可由错误控制器处理。 这可以扩展为添加可能与应用程序业务逻辑(例如,持久防火墙)相关的原始请求(甚至是完整请求本身)中可用的任何请求头,参数或属性。

 /** * A custom exception for situations where a request is blocked or rejected. */ public class RequestBlockedException extends RuntimeException { private static final long serialVersionUID = 1L; /** * The requested URL. */ private String requestUrl; /** * The remote address of the client making the request. */ private String remoteAddress; /** * A message or reason for blocking the request. */ private String reason; /** * The user agent supplied by the client the request. */ private String userAgent; /** * Creates a new Request Blocked Exception. * * @param reqUrl The requested URL. * @param remoteAddr The remote address of the client making the request. * @param userAgent The user agent supplied by the client making the request. * @param message A message or reason for blocking the request. */ public RequestBlockedException(final String reqUrl, final String remoteAddr, final String userAgent, final String message) { this.requestUrl = reqUrl; this.remoteAddress = remoteAddr; this.userAgent = userAgent; this.reason = message; return; } /** * Gets the requested URL. * * @return A URL. */ public String getRequestUrl() { return this.requestUrl; } /** * Gets the remote address of the client making the request. * * @return A remote address. */ public String getRemoteAddress() { return this.remoteAddress; } /** * Gets the user agent supplied by the client making the request. * * @return A user agent string. */ public String getUserAgent() { return this.userAgent; } /** * Gets the reason for blocking the request. * * @return A message or reason for blocking the request. */ public String getReason() { return this.reason; } } 

FirewallInterceptor.java

在Spring Securityfilter运行之后调用此拦截器(即,在AnnotatingHttpFirewall标记了应该被拒绝的请求之后。此拦截器检测请求上的那些标志(属性)并引发我们的错误控制器可以处理的自定义exception。

 import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; /** * Intercepts requests that were flagged as rejected by the firewall. */ public final class FirewallInterceptor implements HandlerInterceptor { /** * Default constructor. */ public FirewallInterceptor() { return; } @Override public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler) throws Exception { if (Boolean.TRUE.equals(request.getAttribute(AnnotatingHttpFirewall.HTTP_HEADER_REQUEST_REJECTED))) { // Throw a custom exception that can be handled by a custom error controller. final String reason = (String) request.getAttribute(AnnotatingHttpFirewall.HTTP_HEADER_REQUEST_REJECTED_REASON); throw new RequestRejectedByFirewallException(request.getRequestURL().toString(), request.getRemoteAddr(), request.getHeader(HttpHeaders.USER_AGENT), reason); } return true; // Allow the request to proceed normally. } @Override public void postHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler, final ModelAndView modelAndView) throws Exception { return; } @Override public void afterCompletion(final HttpServletRequest request, final HttpServletResponse response, final Object handler, final Exception ex) throws Exception { return; } } 

WebConfig.java

WebConfig ,将FirewallInterceptor添加到注册表中。

 @EnableWebMvc public class WebConfig extends WebMvcConfigurerAdapter { /** * Among your other methods in this class, make sure you register * your Interceptor. */ @Override public void addInterceptors(final InterceptorRegistry registry) { // Register firewall interceptor for all URLs in webapp. registry.addInterceptor(new FirewallInterceptor()).addPathPatterns("/**"); return; } } 

ErrorController.java

这专门处理上面的自定义exception,并为客户端生成一个干净的错误页面,同时记录所有相关信息并为自定义应用程序防火墙调用任何特殊的业务逻辑。

 import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; import org.springframework.web.servlet.NoHandlerFoundException; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseStatus; import RequestBlockedException; @ControllerAdvice public final class ErrorController { /** * Logger. */ private static final Logger LOGGER = Logger.getLogger(ErrorController.class.getName()); /** * Generates an Error page by intercepting exceptions generated from AnnotatingHttpFirewall. * * @param request The original HTTP request. * @param ex A RequestBlockedException exception. * @return The tile definition name for the page. */ @ExceptionHandler(RequestBlockedException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) public String handleRequestBlockedException(final RequestBlockedException ex) { if (LOGGER.isLoggable(Level.WARNING)) { LOGGER.log(Level.WARNING, "Rejected request from " + ex.getRemoteAddress() + " for [" + ex.getRequestUrl() + "]. Reason: " + ex.getReason()); } // Note: Perform any additional business logic or logging here. return "errorPage"; // Returns a nice error page with the specified status code. } /** * Generates a Page Not Found page. * * @param ex A NoHandlerFound exception. * @return The tile definition name for the page. */ @ExceptionHandler(NoHandlerFoundException.class) @ResponseStatus(HttpStatus.NOT_FOUND) public String handleException(final NoHandlerFoundException ex) { return "notFoundPage"; } } 

FirewallController.java

具有抛出NoHandlerFoundException的默认映射的NoHandlerFoundException 。 这绕过了DispatcherServlet.noHandlerFound中的鸡与蛋策略,允许该方法始终找到映射,以便始终调用FirewallInterceptor.preHandle 。 这使得RequestRejectedByFirewallException优先于NoHandlerFoundException

为什么这是必要的:

正如这里提到的,当从DispatcherServlet抛出NoHandlerFoundException时(即,当请求的URL没有相应的映射时),没有办法处理从上面的防火墙生成的exception(在调用preHandle()之前抛出NoHandlerFoundException ),所以那些请求将落到你的404视图中(在我的情况下这不是所希望的行为 – 你会看到很多“没有找到带有URI的HTTP请求的映射…”消息)。 这可以通过将特殊标头的检查移动到noHandlerFound方法来解决。 遗憾的是,如果不从头开始编写新的Dispatcher Servlet,就没有办法做到这一点,然后你也可以扔掉整个Spring Framework。 由于受保护,私有和最终方法的混合以及其属性不可访问(无getter或setter)这一事实,因此无法扩展DispatcherServlet 。 包装类也是不可能的,因为没有可以实现的通用接口。 此类中的默认映射提供了一种优雅的方法来规避所有逻辑。

重要提示 :下面的RequestMapping将阻止静态资源的解析,因为它优先于所有已注册的ResourceHandler。 我仍在为此寻找解决方法,但有一种可能性是尝试使用本答案中建议的处理静态资源的方法之一。

 import org.springframework.http.HttpHeaders; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.NoHandlerFoundException; @Controller public final class FirewallController { /** * The name of the model attribute (or request parameter for advertisement click tracking) that contains the request URL. */ protected static final String REQUEST_URL = "requestUrl"; /** * The name of the model attribute that contains the request method. */ protected static final String REQUEST_METHOD = "requestMethod"; /** * The name of the model attribute that contains all HTTP headers. */ protected static final String REQUEST_HEADERS = "requestHeaders"; /** * Default constructor. */ public FirewallController() { return; } /** * Populates the request URL model attribute from the HTTP request. * * @param request The HTTP request. * @return The request URL. */ @ModelAttribute(REQUEST_URL) public final String getRequestURL(final HttpServletRequest request) { return request.getRequestURL().toString(); } /** * Populates the request method from the HTTP request. * * @param request The HTTP request. * @return The request method (GET, POST, HEAD, etc.). */ @ModelAttribute(REQUEST_METHOD) public final String getRequestMethod(final HttpServletRequest request) { return request.getMethod(); } /** * Gets all headers from the HTTP request. * * @param request The HTTP request. * @return The request headers. */ @ModelAttribute(REQUEST_HEADERS) public final HttpHeaders getRequestHeaders(final HttpServletRequest request) { return FirewallController.headers(request); } /** * A catch-all default mapping that throws a NoHandlerFoundException. * This will be intercepted by the ErrorController, which allows preHandle to work normally. * * @param requestMethod The request method. * @param requestUrl The request URL. * @param requestHeaders The request headers. * @throws NoHandlerFoundException every time this method is invoked. */ @RequestMapping(value = "/**") // NOTE: This prevents resolution of static resources. Still looking for a workaround for this. public void getNotFoundPage(@ModelAttribute(REQUEST_METHOD) final String requestMethod, @ModelAttribute(REQUEST_URL) final String requestUrl, @ModelAttribute(REQUEST_HEADERS) final HttpHeaders requestHeaders) throws NoHandlerFoundException { throw new NoHandlerFoundException(requestMethod, requestUrl, requestHeaders); } /** * Gets all headers from a HTTP request. * * @param request The HTTP request. * @return The request headers. */ public static HttpHeaders headers(final HttpServletRequest request) { final HttpHeaders headers = new HttpHeaders(); for (Enumeration names = request.getHeaderNames(); names.hasMoreElements();) { final String headerName = (String) names.nextElement(); for (Enumeration headerValues = request.getHeaders(headerName); headerValues.hasMoreElements();) { headers.add(headerName, (String) headerValues.nextElement()); } } return headers; } } 

结果

当这两个部分都正常工作时,您将看到记录以下两个警告(第一个是Spring Security,第二个是Spring Framework(Core) ErrorController )。 现在,您可以完全控制日志记录,并可以根据需要调整可扩展的应用程序防火墙。

 Sep 12, 2018 10:24:37 AM com.mycompany.spring.security.AnnotatingHttpFirewall getFirewalledRequest WARNING: Intercepted org.springframework.security.web.firewall.RequestRejectedException: Remote Host: 0:0:0:0:0:0:0:1 User Agent: Mozilla/5.0 (Windows NT 6.3; Win64; x64; rv:62.0) Gecko/20100101 Firefox/62.0 Request URL: http://localhost:8080/webapp-www-mycompany-com/login.php Sep 12, 2018 10:24:37 AM com.mycompany.spring.controller.ErrorController handleException WARNING: Rejected request from 0:0:0:0:0:0:0:1 for [http://localhost:8080/webapp-www-mycompany-com/login.php]. Reason: The request was rejected because it is a likely vulnerability scan. 

它也可以通过简单的filter处理,这将导致404错误响应

 @Component @Slf4j @Order(Ordered.HIGHEST_PRECEDENCE) public class LogAndSuppressRequestRejectedExceptionFilter extends GenericFilterBean { @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { try { chain.doFilter(req, res); } catch (RequestRejectedException e) { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; log .warn( "request_rejected: remote={}, user_agent={}, request_url={}", request.getRemoteHost(), request.getHeader(HttpHeaders.USER_AGENT), request.getRequestURL(), e ); response.sendError(HttpServletResponse.SC_NOT_FOUND); } } }