Spring Security的Servlet支持是基于Servlet过滤器的,因此首先了解过滤器的一般作用是很有帮助的。下图显示了单个HTTP请求处理程序的典型分层结构。
处理客户端发送的请求时,容器创建一个FilterChain,其中包含Filter实例和Servlet,根据请求URI的路径处理HttpServletRequest。在Spring MVC应用程序中,Servlet是DispatcherServlet的一个实例。最多一个Servlet可以处理单个HttpServletRequest和HttpServletResponse。然而,可以使用多个过滤器来:
Filter 的功能,在于FilterChain 里面的filter。
FilterChain 使用案例
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
// do something before the rest of the application
chain.doFilter(request, response); // invoke the rest of the application
// do something after the rest of the application
}
由于过滤器仅影响下游过滤器实例和Servlet,因此调用每个过滤器的顺序非常重要。
Spring提供了一个名为DelegatingFilterProxy的Filter实现,它允许在Servlet容器的生命周期和Spring的ApplicationContext之间进行桥接。Servlet容器允许使用其自己的标准注册Filter实例,但它不知道Spring定义的Bean。您可以通过标准的Servlet容器机制注册DelegatingFilterProxy,但将所有工作委托给实现Filter的Spring Bean。
这是DelegatingFilterProxy如何适配到Filter实例和FilterChain的图片。
DelegatingFilterProxy会从ApplicationContext中查找名为Filter0的Bean,然后调用Filter0 Bean。以下是DelegatingFilterProxy的伪代码示例:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
Filter delegate = getFilterBean(someBeanName);
delegate.doFilter(request, response);
}
DelegatingFilterProxy的另一个好处是它允许延迟查找Filter bean实例。这很重要,因为容器在启动之前需要注册Filter实例。然而,Spring通常使用ContextLoaderListener来加载Spring Beans,这个过程直到Filter实例需要被注册之后才会完成。
Spring Security的Servlet支持包含在FilterChainProxy中。FilterChainProxy是Spring Security提供的特殊Filter,它通过SecurityFilterChain允许委派给许多Filter实例。由于FilterChainProxy是一个Bean,通常会被包装在DelegatingFilterProxy中。
SecurityFilterChain被FilterChainProxy用来确定当前请求应该调用哪些Spring Security过滤器实例。
以下图片展示了SecurityFilterChain的作用。
SecurityFilterChain中的安全过滤器通常是Bean,但它们是注册到FilterChainProxy而不是DelegatingFilterProxy。FilterChainProxy相比直接注册到Servlet容器或DelegatingFilterProxy,提供了许多优势。首先,它为Spring Security的所有Servlet支持提供了一个起点。因此,如果您尝试解决Spring Security的Servlet支持问题,在FilterChainProxy中设置一个调试点是一个很好的起点。
第二,由于FilterChainProxy在Spring Security的使用中是至关重要的,它可以执行一些被视为非可选的任务。例如,它清除SecurityContext以避免内存泄漏。它还应用了Spring Security的HttpFirewall来保护应用程序免受某些类型的攻击。
此外,它在确定何时应调用SecurityFilterChain时提供了更多的灵活性。在Servlet容器中,过滤器实例仅根据URL调用。然而,FilterChainProxy可以通过使用RequestMatcher接口根据HttpServletRequest中的任何内容来确定调用。
以下图片显示了多个SecurityFilterChain实例。
在多个SecurityFilterChain图中,FilterChainProxy决定使用哪个SecurityFilterChain。只有第一个匹配的SecurityFilterChain会被调用。如果请求的URL是/api/messages/,它首先匹配SecurityFilterChain-0的模式/api/,所以只有SecurityFilterChain0被调用,即使它也匹配SecurityFilterChain-n。如果请求的URL是/messages/,它不匹配SecurityFilterChain-0的模式/api/,所以FilterChainProxy会继续尝试每个SecurityFilterChain。假设没有其他SecurityFilterChain实例匹配,那么SecurityFilterChain-n会被调用。
请注意,SecurityFilterChain-0只配置了三个安全过滤器实例。然而,SecurityFilterChain-n配置了四个安全过滤器实例。重要的是要注意,每个SecurityFilterChain都可以是独特的,并且可以独立配置。实际上,如果应用程序希望Spring Security忽略某些请求,SecurityFilterChain可能不包含任何安全过滤器实例。