Spring Security 是一个强大且灵活的安全框架,其核心功能包括身份认证、授权和防御常见攻击。要深入了解其底层原理,需要理解以下关键概念和组件。而Spring Security的底层原理是传统的 Servlet过滤器 ,欲了解更多详细信息,您可以访问 Architecture :: Spring Security

1. A Review of Filters

下图展示了处理一个Http请求时,过滤器和Servlet的工作流程

filterchain

客户端向应用程序发送请求,容器根据请求 URI 的路径创建一个FilterChain ,其中包含应处理HttpServletRequestFilter实例和Servlet 。在 Spring MVC 应用程序中,ServletDispatcherServlet的实例。一个Servlet最多可以处理一个HttpServletRequestHttpServletResponse 。然而,多个Filter可用于:

  • 防止调用下游Filter实例或Servlet 。在这种情况下, Filter通常会写入HttpServletResponse
  • 修改下游Filter实例和Servlet使用的HttpServletRequestHttpServletResponse

FilterChain的使用示例:

1
2
3
4
5
6
public void doFilter(ServletRequest request, 
ServletResponse response, FilterChain chain) {
// do something before the rest of the application
chain.doFilter(request, response);
// do something after the rest of the application
}

由于Filter仅影响下游Filter实例和Servlet ,因此调用每个Filter顺序非常重要。

2. DelegatingFilterProxy

Spring 提供了一个名为DelegatingFilterProxyFilter实现,它允许在 Servlet 容器的生命周期和 Spring 的ApplicationContext之间进行桥接。 Servlet容器允许使用自己的标准注册Filter实例,但它不知道Spring定义的Bean。可以通过标准 Servlet 容器机制注册DelegatingFilterProxy ,但将所有工作委托给实现Filter Spring Bean。

delegatingfilterproxy

DelegatingFilterProxyApplicationContext中查找 Bean Filter 0 ,然后调用 Bean Filter 0。

1
2
3
4
5
public void doFilter(ServletRequest request, 
ServletResponse response, FilterChain chain) {
Filter delegate = getFilterBean(someBeanName); // 延迟获取注册为 Spring Bean 的 Filter
delegate.doFilter(request, response); // 将工作委托给 Spring Bean
}

DelegatingFilterProxy的另一个好处是它允许延迟查找Filter bean 实例。这很重要,因为容器需要在容器启动之前注册Filter实例。然而,Spring 通常使用ContextLoaderListener来加载 Spring Bean,直到需要注册Filter实例之后才会完成此操作。

3. FilterChainProxy

Spring Security 的 Servlet 支持包含在FilterChainProxy中。 FilterChainProxy是 Spring Security 提供的一个特殊Filter ,它允许通过 SecurityFilterChain 委托给许多Filter实例。由于FilterChainProxy是一个 Bean,因此它通常包装在 DelegatingFilterProxy 中。

delegatingfilterproxy

4. SecurityFilterChain

FilterChainProxy使用SecurityFilterChain来确定应为当前请求调用哪些 Spring Security Filter实例。

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 调用Filter实例。但是, FilterChainProxy可以使用RequestMatcher接口根据HttpServletRequest中的任何内容确定调用。

multi-securityfilterchain

在上图中, FilterChainProxy决定应使用哪个SecurityFilterChain 。仅调用第一个匹配的SecurityFilterChain 。如果请求/api/messages/的 URL,它首先匹配/api/**SecurityFilterChain 0模式,因此仅调用SecurityFilterChain 0 ,即使它也匹配SecurityFilterChain n 。如果请求/messages/的 URL,则它与/api/**SecurityFilterChain 0模式不匹配,因此FilterChainProxy继续尝试每个SecurityFilterChain 。假设没有其他SecurityFilterChain实例匹配,则调用SecurityFilterChain nSecurityFilterChain 0仅配置了三个安全Filter实例。然而, SecurityFilterChain n配置了四个安全Filter实例。需要注意的是,每个SecurityFilterChain都可以是唯一的,并且可以单独配置。事实上,如果应用程序希望 Spring Security 忽略某些请求, SecurityFilterChain安全Filter实例可能为零。

5. Security Filters

安全过滤器通过SecurityFilterChain API 插入到FilterChainProxy中。这些过滤器可用于多种不同的目的,例如身份验证授权漏洞保护等。过滤器按照特定的顺序执行,以保证它们在正确的时间被调用,例如,执行身份验证的Filter应该在执行授权的Filter之前被调用。通常不需要知道 Spring Security 的Filter的顺序。但是,有时了解顺序是有好处的,如果您想了解它们,可以检查FilterOrderRegistration代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Configuration
@EnableWebSecurity
public class SecurityConfig {

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(Customizer.withDefaults())
.authorizeHttpRequests(authorize -> authorize
.anyRequest().authenticated()
)
.httpBasic(Customizer.withDefaults())
.formLogin(Customizer.withDefaults());
return http.build();
}

}