HttpFirewall简介
在 Servlet 容器规范中,为 HttpServletRequest 定义了一些属性,如 contextPath、servletPath、pathInfo、queryString 等,这些属性都可以通过 get 方法获取。
然而,在 Servlet 容器规范中并没有定义这些属性可以包含哪些值,例如在 servletPath 和 pathInfo 中都可以包含 RFC2396 规范(https://www.ietf.org/rfc/rfc2396.txt)中定义的参数,不同容器对外理方案带不同,有的容器会对进行预外理,有的容器则不会。这种比较混乱的处理方式有可能会造成安全隐患,因此 Spring Security 中通过 HttpFirewall 来检查请求路径以及参数是否合法,如果合法,才会进入到过滤器链中进行处理。
HttpFirewall 是一个接口,它只有两个方法:
public interface HttpFirewall {
FirewalledRequest getFirewalledRequest(HttpServletRequest request)
throws RequestRejectedException;
HttpServletResponse getFirewalledResponse(HttpServletResponse response);
}
getFirewalledRequest 方法用来对请求对象进行检验并封装,getFirewalledResponse 方法则对响应对象进行封装。
FirewalledRequest是封装后的请求类,但实际上该类只是在 HttpServletRequestWrapper 的基础上增加了 reset 方法。当 Spring Security 过滤器链执行完毕时,由 FilterChainProxy 负责调用该 reset 方法,以便重置全部或者部分属性。
FirewalledResponse 是封装后的响应类,该类主要重写了 sendRedirect、setHeader、addHeader 以及 addCookie 四个方法,在每一个方法中都对其参数进行校验,以确保参数中不含有 \r 和 \n。
HttpFirewall 一共有两个实现类,如图8-1所示,

-
DefaultHttpFirewall:虽然名字中包含 Default,但这并不是框架默认使用的 Http 防火墙,它只是一个检查相对宽松的防火墙。
-
StrictHttpFirewall:这是一个检查严格的 Http 防火墙,也是框架默认使用的 Http 防火墙。
HttpFirewall 中对请求的合法性校验在 FilterChainProxy#doFilterInternal 方法中触发,具体可以参考 4.1.4 小节,这里不再资述,
需要注意的是 HttpFirewall 的配置位置,在 Spring Security 框架中有两个地方涉及了 HttpFirewall 实例的获取:
-
在 FilterChainProxy 属性定义中,默认创建的 HttpFirewall 实例就是 StrictHtpFirewall
-
FilterChainProxy 是在 WebSecurity#performBuild 方法中构建的,而 WebSecurity 实现了 ApplicationContextAware 接口,并实现了接口中的 setApplicationContext 方法,在该方法中,从 Spring 容器中查找到 HttpFirewall 对象并赋值给 httpFirewall 属性。最终在 performBuild 方法中,将 FilterChainProxy 对象构建成功后,如果 httpFirewall 不为 null,就把 httpFirewall 配置给 FilterChainProxy 对象。
根据以上两点可以得出:如果 Spring 容器中存在 HttpFirewall 实例,则最终使用 Spring 容器提供的 HttpFirewall 实例;如果 Spring 容器中不存在 HttpFirewall 实例,则使用 FilterChainProxy 中默认定义的 StrictHttpFirewall。进而可知,如果开发者不想使用默认的 StrictHttpFirewall 实例,则只需要自己已提供一个 HttpFirewall 并注册到 Spring 容器中即可。