Spring (32)Spring Security的过滤器链

63 阅读3分钟

Spring Security 的安全模型核心之一是一系列过滤器,这些过滤器组成了一个链。这个过滤器链负责处理进入应用的每个HTTP请求,实现认证、授权等安全功能。每个过滤器都有其特定的责任,它们按照特定的顺序执行。

过滤器链概念

在 Spring Security 中,过滤器链是通过 FilterChainProxy 类来管理的。FilterChainProxy 包含多个 SecurityFilterChain 实例,每个 SecurityFilterChain 包含一系列的过滤器。当一个请求到来时,FilterChainProxy 会根据请求的URL决定使用哪一个 SecurityFilterChain,然后依次执行该链中的过滤器。

核心过滤器

Spring Security 中的几个关键过滤器包括:

  • SecurityContextPersistenceFilter: 保持安全上下文(SecurityContext)在请求之间的持久化。通常用于从Session中加载和存储安全上下文。
  • UsernamePasswordAuthenticationFilter: 处理基于表单的登录请求。
  • BasicAuthenticationFilter: 处理基于HTTP基本认证的请求。
  • ExceptionTranslationFilter: 捕获安全异常,包括认证失败和访问被拒绝的异常,并根据配置进行相应处理(如重定向到登录页面)。
  • FilterSecurityInterceptor: 最终的授权决策过滤器,它会在调用目标资源之前检查 AccessDecisionManager 是否允许当前用户访问请求的资源。

示例配置

在 Spring Boot 应用中,可以通过继承 WebSecurityConfigurerAdapter 并重写 configure(HttpSecurity) 方法来自定义安全配置,包括过滤器链的配置。以下是一个简单的示例,它演示了如何自定义哪些路径受保护,哪些不受保护:

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/", "/home").permitAll() // 允许所有人访问"/"和"/home"
                .anyRequest().authenticated() // 除了上面定义的URL外,所有请求都需要认证
            .and()
            .formLogin() // 启用表单登录
                .loginPage("/login") // 自定义登录页面URL
                .permitAll() // 允许所有人访问登录页面
            .and()
            .logout() // 启用注销
                .permitAll(); // 允许所有人访问注销页面
    }
}

深入源码

在源码中,FilterChainProxy 是整个过滤器链的入口点。它被定义为一个Servlet过滤器,负责委托给配置的 SecurityFilterChainFilterChainProxy 的初始化过程中会加载 SecurityConfig 中定义的过滤器和相关配置。

public class FilterChainProxy extends GenericFilterBean {

    private List<SecurityFilterChain> filterChains;

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        // 根据请求找到对应的SecurityFilterChain
        SecurityFilterChain secChain = getSecurityFilterChain(request);
        if (secChain != null) {
            secChain.doFilter(request, response, chain);
        } else {
            // 如果没有找到匹配的FilterChain,继续执行Servlet容器默认的Filter链
            chain.doFilter(request, response);
        }
    }

    private SecurityFilterChain getSecurityFilterChain(ServletRequest request) {
        for (SecurityFilterChain chain : filterChains) {
            if (chain.matches(request)) {
                return chain;
            }
        }
        return null;
    }
}

SecurityFilterChaindoFilter 方法会依次调用链中的每个过滤器。每个过滤器执行完毕后,一般会调用 FilterChaindoFilter 方法,将控制权传递给链中的下一个过滤器,直到所有过滤器执行完毕。

这里的源码只是一个简化的视角,实际的实现可能会更复杂,因为涉及到的类和接口较多,但基本原理和流程是一致的。通过上述分析,你应该能够获得对 Spring Security 过滤器链如何工作的深入理解。