SpringBoot 2.0 ErrorpageFilter disable

最近在Monitor生产环境时发现有些时段出现了大量的http 500异常,刚开始还以为是出现了问题,仔细排查过后发现其实是遇到了Sql注入的攻击,被Spring Security的拦截器拦截后抛出了RequestRejectedException的异常,由于在filter层,该异常未被处理最终成了500。虽然未造成真实的影响,但是影响到了Monitor的准确性,还是要进行处理。

方案一: RequestRejectedHandler

如果是SpringBoot 2.4,有一个很方便的解决方案

	@Bean
	public RequestRejectedHandler requestRejectedHandler() {
		// It is because of cannot catching this exception in OncePerRequestFilter.doFilterInternal in pure Tomcat Container.
		// (But can catch it in Spring Boot embedded Tomcat Container.)

		// In order to use RequestRejectedHandler, we need to upgrade spring security from 5.3.5.RELEASE to 5.4.0
		// Refer to: https://github.com/spring-projects/spring-security/pull/7052

		// Sends an error response with a configurable status code (default is 400 BAD_REQUEST)
		// We can pass a different value in following constructor
		return new HttpStatusRequestRejectedHandler();
	}

这个Handler内部会之间返回http400,但是升级Spring风险较大,评估之后采用了如下方案:

增加Filter

Spring Security的优先级较低,在上层增加Filter对专门的Exception进行catche来处理

public class UnHandleExceptionFilter implements Filter {

    private static final Logger logger = LoggerFactory.getLogger(UnHandleExceptionFilter.class);

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {

        try {
            chain.doFilter(servletRequest, servletResponse);
        }catch (RequestRejectedException e){
            logger.error("",e);
            HttpServletResponse response = (HttpServletResponse) servletResponse;
            ExceptionResp exceptionResp = ExceptionResp.ACL_ACCESS_IS_DENIED;
            
            response.setStatus(exceptionResp.getStatus());
            response.setContentType("application/json");
            MBSExceptionResp mbsexceptionResp = new MBSExceptionResp(exceptionResp);
            mbsexceptionResp.addError(e.getMessage());
            String msg= JsonUtil.toStr(mbsexceptionResp);
            PrintWriter writer = response.getWriter();
            writer.write(msg);
        }
    }

    @Override
    public void destroy() {

    }
}

该方法实现起来也很简单,本地以SpringBoot启动也不会有问题,但是我们实际上是打包成War包的形式进行部署,上到测试环境时该方案失效,并有如下异常栈:

[2021-09-01 09:16:06,740] [ERROR] 26544 [http-apr-8081-exec-266] o.s.b.w.s.s.ErrorPageFilter              - ss11dd - Forwarding to error page from request [/siteservice/v1/sites/123"] due to exception [The request was rejected because the URL contained a potentially malicious String ";"] org.springframework.security.web.firewall.RequestRejectedException: The request was rejected because the URL contained a potentially malicious String ";"
        ... suppressed 119 lines
        at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:97) ~[spring-boot-actuator-2.3.12.RELEASE.jar:2.3.12.RELEASE]
        ... suppressed 16 lines
        at org.springframework.boot.web.servlet.support.ErrorPageFilter.doFilter(ErrorPageFilter.java:126) ~[spring-boot-2.3.12.RELEASE.jar:2.3.12.RELEASE]
        at org.springframework.boot.web.servlet.support.ErrorPageFilter.access$000(ErrorPageFilter.java:64) ~[spring-boot-2.3.12.RELEASE.jar:2.3.12.RELEASE]
        at org.springframework.boot.web.servlet.support.ErrorPageFilter$1.doFilterInternal(ErrorPageFilter.java:101) ~[spring-boot-2.3.12.RELEASE.jar:2.3.12.RELEASE]
        ...
        at org.springframework.boot.web.servlet.support.ErrorPageFilter.doFilter(ErrorPageFilter.java:119) ~[spring-boot-2.3.12.RELEASE.jar:2.3.12.RELEASE]
        ... suppressed 15 lines
        at com.XXXXX.apiserver.common.filter.SqlInsertExceptionFilter.doFilter(SqlInsertExceptionFilter.java:32) ~[classes/:41.10.0.sqlInsertError-18608]
        ... suppressed 32 lines
        at com.XXXXX.service.common.log.TrackingIdFilter.doFilter(TrackingIdFilter.java:36) ~[XXXXX-service-common-2.11.0-SNAPSHOT.jar:?]
        ... suppressed 24 lines
        at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:616) ~[tomcat-coyote.jar:8.5.64]
        at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) ~[tomcat-coyote.jar:8.5.64]
        at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:831) ~[tomcat-coyote.jar:8.5.64]
        at org.apache.tomcat.util.net.AprEndpoint$SocketWithOptionsProcessor.run(AprEndpoint.java:2045) ~[tomcat-coyote.jar:8.5.64]

可以看到多了一层ErrorpageFilter,再看这个filter的内部

	private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		ErrorWrapperResponse wrapped = new ErrorWrapperResponse(response);
		try {
			chain.doFilter(request, wrapped);
			if (wrapped.hasErrorToSend()) {
				handleErrorStatus(request, response, wrapped.getStatus(), wrapped.getMessage());
				response.flushBuffer();
			}
			else if (!request.isAsyncStarted() && !response.isCommitted()) {
				response.flushBuffer();
			}
		}
		catch (Throwable ex) {
			Throwable exceptionToHandle = ex;
			if (ex instanceof NestedServletException) {
				Throwable rootCause = ((NestedServletException) ex).getRootCause();
				if (rootCause != null) {
					exceptionToHandle = rootCause;
				}
			}
			handleException(request, response, wrapped, exceptionToHandle);
			response.flushBuffer();
		}
	}

	private void handleException(HttpServletRequest request, HttpServletResponse response, ErrorWrapperResponse wrapped,
			Throwable ex) throws IOException, ServletException {
		Class<?> type = ex.getClass();
		String errorPath = getErrorPath(type);
		if (errorPath == null) {
			rethrow(ex);
			return;
		}
		if (response.isCommitted()) {
			handleCommittedResponse(request, ex);
			return;
		}
		forwardToErrorPage(errorPath, request, wrapped, ex);
	}

如果遇到异常的话,会根据Exception.class,来获取对应的ErrorPage的Path,最终Forward到对应的页面,没有再向外抛出异常。

为了重新取回对异常的控制权,这里有两种方式:
1.ErrorpageFilter有一个addErrorPages的方法,为特定的Excpetion设置特定的Path和Controller进行处理
2.disable ErrorpageFilter,使用自己的ExceptionHandlerFilter。

这里为了方便起见,采用了第二种方式,disable ErrorpageFilter在SpringBoot1.0版本和2.0中是不一样的,公司使用的是2.0

public class Application extends SpringBootServletInitializer implements ApplicationContextAware {


    static private ApplicationContext ctx;

    public Application(){
        super();
        setRegisterErrorPageFilter(false);
    }


    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        ctx = applicationContext;
    }

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(Application.class);
    }
}

禁用之后,再对拦截器进行简单修改,拦截所有的Exception,然后针对特定的进行自己的处理

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值