我们以Spring Security中Servlet实现方式来介绍Spring Security整体架构,不会具体到具体的认证、授权来介绍Spring Security架构。如果让我们自己来写一个安全框架,我们需要将我们的框架置于项目中的哪个位置?很显然是在所有的请求到达Controller之前,在访问具体的某个Controller之前,对请求进行认证、授权、安全验证。在JakartaEE(JavaEE的新版)规范中,Filter和Servlet都符合这个前置要求。在Spring MVC中基本上只包含一个DispatcherServelt
主要用于请求分发,缺乏安全相关的支持和合适的扩展机制。我们看Spring Security官方文档中的图来理解:
客户端向应用程序发送一个请求,容器创建一个 FilterChain
,其中包含 Filter
实例和 Servlet
,应该根据请求URI的路径来处理 HttpServletRequest
。在Spring MVC应用程序中,Servlet是 DispatcherServlet
的一个实例。一个 Servlet
最多可以处理一个 HttpServletRequest
和 HttpServletResponse
。然而,可以使用多个 Filter
来完成如下工作。本质上,Spring Security的实现原理很简单,就是提供了一个用于安全验证的Filter链
DelegatingFilterProxy
Spring 提供了一个名为 DelegatingFilterProxy
的 Filter
实现,它在Spring Security中起到了一个重要的桥梁作用,连接了Servlet容器和Spring容器。Servlet容器不了解Spring定义的Beans,而Spring Security的大部分组件及其依赖都是注册到Spring容器中的Bean。DelegatingFilterProxy
核心代码的主要工作就是从WebApplicationContext
获取指定名称的Filter Bean,然后委托给这个Bean的doFilter
方法。
DelegatingFilterProxy
从 ApplicationContext
查找 Bean Filter0,然后调用 Bean Filter0。下面是Spring Security提供的一段伪代码,快要帮助我们理解:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
Filter delegate = getFilterBean(someBeanName); // 1
delegate.doFilter(request, response); // 2
}
- 延迟地获取被注册为Spring Bean的 Filter。 对于
DelegatingFilterProxy
中的例子,delegate
是 Bean Filter0 的一个实例。 - 将工作委托给 Spring Bean。
FilterChainProxy
被委托的类型就是FilterChainProxy
,FilterChainProxy
是 Spring Security 提供的一个特殊的 Filter
,允许通过 SecurityFilterChain
委托给许多 Filter
实例。由于 FilterChainProxy
是一个Bean,它通常被包裹在 DelegatingFilterProxy
中。FilterChainProxy
主要作用就是查找匹配当前Http请求规则的SecurityFilterChain
,然后将工作委派给SecurityFilterChain
的所有Filter。
SecurityFilterChain
SecurityFilterChain
被 FilterChainProxy
用来确定当前请求应该调用哪些 Spring Security Filter
实例。
SecurityFilterChain
中的 Security Filter
通常是Bean,但它们是用 FilterChainProxy
而不是 DelegatingFilterProxy
注册的。与直接向Servlet容器或 DelegatingFilterProxy
注册相比,FilterChainProxy
有很多优势。首先,它为 Spring Security 的所有 Servlet 支持提供了一个起点。由于这个原因,如果你试图对 Spring Security 的 Servlet 支持进行故障诊断,在 FilterChainProxy
中添加一个调试点是一个很好的开始。
其次,由于 FilterChainProxy
是 Spring Security 使用的核心,它可以执行一些不被视为可有可无的任务。 例如,它清除了 SecurityContext
以避免内存泄漏。它还应用Spring Security的 HttpFirewall
来保护应用程序免受某些类型的攻击。
此外,它在确定何时应该调用 SecurityFilterChain
方面提供了更大的灵活性。在Servlet容器中,Filter
实例仅基于URL被调用。 然而,FilterChainProxy
可以通过使用 RequestMatcher
接口,根据 HttpServletRequest
中的任何内容确定调用。
在 Multiple SecurityFilterChain
图中, FilterChainProxy
决定应该使用哪个 SecurityFilterChain
。只有第一个匹配的 SecurityFilterChain
被调用。如果请求的URL是 /api/messages/
,它首先与 /api/**
的 SecurityFilterChain0
模式匹配,所以只有 SecurityFilterChain0
被调用,尽管它也与 SecurityFilterChainn
匹配。如果请求的URL是 /messages/
,它与 /api/**
的 SecurityFilterChain0
模式不匹配,所以 FilterChainProxy
继续尝试每个 SecurityFilterChain
。假设没有其他 SecurityFilterChain
实例相匹配,则调用 SecurityFilterChainn
。
请注意,SecurityFilterChain0
只配置了三个 security Filter
实例。然而,SecurityFilterChainn
却配置了四个 security Filter
实例。值得注意的是,每个 SecurityFilterChain
都可以是唯一的,并且可以单独配置。事实上,如果应用程序希望 Spring Security 忽略某些请求,那么一个 SecurityFilterChain
可能会有零个 security Filter
实例。
Security Filter
Security Filter 是通过 SecurityFilterChain
API 插入 [FilterChainProxy
中的。
这些 filter 可以用于许多不同的目的,如 认证
、 授权
、 漏洞保护
等等。filter 是按照特定的顺序执行的,以保证它们在正确的时间被调用,例如,执行认证的 Filter
应该在执行授权的 Filter
之前被调用。一般来说,没有必要知道 Spring Security 的 Filter
的顺序。但是,有些时候知道顺序是有好处的,如果你想知道它们,可以查看 FilterOrderRegistration
代码。
源码分析
通过DefaultSecurityFilterChain
可以看到,默认情况下会引入16个Filter。比如:UsernamePasswordAuthenticationFilter
支持Form表单形式的身份验证模块。