跳至主要內容

初识 Spring Security 底层原理

Jin大约 4 分钟

初识 Spring Security 底层原理

参考

1、Filter

Spring Security 对 Servlet 的支持是基于 Servlet 过滤器的,所以先看一下过滤器的一般作用是很有帮助的。下图显示了单个HTTP请求的处理程序的典型分层。

![filterchain](images/04-Spring Security 底层原理入门/filterchain.png)

客户端向应用程序发送一个请求,容器创建一个 FilterChain,其中包含 Filter 实例和 Servlet,应该根据请求URI的路径来处理 HttpServletRequest。在Spring MVC应用程序中,ServletDispatcherServlet 的一个实例。一个 Servlet 最多可以处理一个 HttpServletRequestHttpServletResponse。然而,可以使用多个 Filter 来完成如下工作。

  • 防止下游的 Filter 实例或 Servlet 被调用。在这种情况下,Filter 通常会使用 HttpServletResponse 对客户端写入响应。
  • 修改下游的 Filter 实例和 Servlet 所使用的 HttpServletRequestHttpServletResponse

过滤器的力量来自于传入它的 FilterChain

2、DelegatingFilterProxy

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

下面是 DelegatingFilterProxy 如何融入 Filter 实例和 FilterChain 的流程。

![delegatingfilterproxy](images/04-Spring Security 底层原理入门/delegatingfilterproxy.png)

DelegatingFilterProxyApplicationContext 查找 Bean Filter0,然后调用 Bean Filter0。下面的列表显示了 DelegatingFilterProxy 的伪代码。

3、FilterChainProxy

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

下图显示了 FilterChainProxy 的作用。

![filterchainproxy](images/04-Spring Security 底层原理入门/filterchainproxy.png)

4、SecurityFilterChain

SecurityFilterChainFilterChainProxy 用来确定当前请求应该调用哪些 Spring Security Filter 实例。

![SecurityFilterChain](images/04-Spring Security 底层原理入门/SecurityFilterChain.png)

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 中的任何内容确定调用。

![multi-securityfilterchain](images/04-Spring Security 底层原理入门/multi-securityfilterchain.png)

在 Multiple SecurityFilterChain 图中, FilterChainProxy 决定应该使用哪个 SecurityFilterChain。只有第一个匹配的 SecurityFilterChain 被调用。如果请求的URL是 /api/messages/,它首先与 /api/**SecurityFilterChain 0 模式匹配,所以只有 SecurityFilterChain 0 被调用,尽管它也与 SecurityFilterChainn 匹配。如果请求的URL是 /messages/,它与 /api/**SecurityFilterChain 0 模式不匹配,所以 FilterChainProxy 继续尝试每个 SecurityFilterChain。假设没有其他 SecurityFilterChain 实例相匹配,则调用 SecurityFilterChain n

请注意,SecurityFilterChain 0 只配置了三个 security Filter 实例。然而,SecurityFilterChain n 却配置了四个 security Filter 实例。值得注意的是,每个 SecurityFilterChain 都可以是唯一的,并且可以单独配置。事实上,如果应用程序希望 Spring Security 忽略某些请求,那么一个 SecurityFilterChain 可能会有零个 security Filter 实例。

5、Security Filter

Security Filter 是通过 SecurityFilterChain API 插入 FilterChainProxy 中的。

这些 filter 可以用于许多不同的目的,如 认证、 授权、 漏洞保护 等等。filter 是按照特定的顺序执行的,以保证它们在正确的时间被调用,例如,执行认证的 Filter 应该在执行授权的 Filter 之前被调用。一般来说,没有必要知道 Spring Security 的 Filter 的顺序。但是,有些时候知道顺序是有好处的,如果你想知道它们,可以查看 FilterOrderRegistration 代码。

@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();
    }

}
  1. 首先,调用 CsrfFilter 来防止 CSRF 攻击。
  2. 其次, 认证 filter 被调用以认证请求。
  3. 第三,调用 AuthorizationFilter 来 授权该请求。

5.1、打印出 Security Filter

很多时候,看到为某一特定请求调用的 security Filter 的列表是很有用的。例如,你想确保 你添加的 filter 在 security filter 的列表中。

查看源码:

org.springframework.security.web.DefaultSecurityFilterChain#DefaultSecurityFilterChain(...)

日志输出源码:

logger.debug(LogMessage.format("Will secure %s with filters: %s", requestMatcher, names));

filter 列表在应用程序启动时以 DEBUG 级别打印,需要配置如下:

logging:
  level:
    org:
      springframework:
        security:
          web: DEBUG

注意事项:6.3.x 之前SpringSecurity版本以 INFO 级别打印,查看官网提交记录:https://github.com/spring-projects/spring-security/commit/ac9bdf5cbfc526a8eaf0f70ab6361bc91892fb3copen in new window

![image-20241119160749049](images/04-Spring Security 底层原理入门/image-20241119160749049.png)

控制台输出内容

![image-20241119161053914](images/04-Spring Security 底层原理入门/image-20241119161053914.png)

Will secure any request with filters: DisableEncodeUrlFilter, WebAsyncManagerIntegrationFilter, SecurityContextHolderFilter, HeaderWriterFilter, CsrfFilter, LogoutFilter, UsernamePasswordAuthenticationFilter, DefaultLoginPageGeneratingFilter, DefaultLogoutPageGeneratingFilter, BasicAuthenticationFilter, RequestCacheAwareFilter, SecurityContextHolderAwareRequestFilter, AnonymousAuthenticationFilter, ExceptionTranslationFilter, AuthorizationFilter
贡献者: Jin