Spring Security 过滤器链的构建过程:从 HttpSecurity 到 DefaultSecurityFilterChain

过滤器链不是凭空出现的,它是由 HttpSecurity 通过 Builder 模式精心构建的。本文将深入剖析 HttpSecurity 的配置收集、排序、构建全流程,以及 AuthenticationManagerBuilder、HttpSecurity、WebSecurity 三者的层级关系。

前言:从一行配置到一个 Filter,中间经历了什么?

在上一篇中,我们了解了 FilterOrderRegistration 如何定义过滤器的执行顺序------那是"宪法"。但宪法只是规定了"谁在前、谁在后",并没有回答一个更根本的问题:

http.formLogin() 这一行配置,最终是如何变成一个活生生的 UsernamePasswordAuthenticationFilter 对象,并被精确地放到过滤器链中正确位置的?

这个问题的答案,藏在 HttpSecurityBuilder 模式 构建过程中。这一过程可以分为四个阶段:

  1. 创建阶段HttpSecurityConfiguration 用原型作用域创建 HttpSecurity 实例
  2. 收集阶段 :每次调用 http.xxx(),背后的 Configurer 被注册到内部列表
  3. 构建阶段http.build() 触发 doBuild 三步曲 ------ init → configure → performBuild
  4. 组装阶段WebSecurity 收集所有 SecurityFilterChain,构建最终的 FilterChainProxy

本文将完整拆解这四个阶段的源码。

一、HttpSecurity:过滤器链的"工厂"

1.1 HttpSecurity 的创建:HttpSecurityConfiguration

HttpSecurity 对象由 HttpSecurityConfiguration 负责创建。这个配置类做了两件关键的事:

第一,以 prototype 作用域创建 HttpSecurity:

java 复制代码
@Bean(HTTPSECURITY_BEAN_NAME)
@Scope("prototype")
HttpSecurity httpSecurity() throws Exception {
    // ① 创建 LazyPasswordEncoder(延迟加载,避免循环依赖)
    LazyPasswordEncoder passwordEncoder = new LazyPasswordEncoder(this.context);

    // ② 构建 AuthenticationManagerBuilder(认证管理器构建器)
    AuthenticationManagerBuilder authenticationBuilder = 
        new DefaultPasswordEncoderAuthenticationManagerBuilder(
            this.objectPostProcessor, passwordEncoder);
    authenticationBuilder.parentAuthenticationManager(authenticationManager());
    authenticationBuilder.authenticationEventPublisher(getAuthenticationEventPublisher());

    // ③ 创建 HttpSecurity 实例
    HttpSecurity http = new HttpSecurity(
        this.objectPostProcessor, authenticationBuilder, createSharedObjects());

    // ④ 手动添加 WebAsyncManagerIntegrationFilter(异步请求安全集成)
    WebAsyncManagerIntegrationFilter webAsyncManagerIntegrationFilter = 
        new WebAsyncManagerIntegrationFilter();
    webAsyncManagerIntegrationFilter.setSecurityContextHolderStrategy(
        this.securityContextHolderStrategy);

    // ⑤ 应用默认配置------这就是我们看到的那些"自动开启"的过滤器的来源!
    http
        .csrf(withDefaults())                    // → CsrfFilter
        .addFilter(webAsyncManagerIntegrationFilter)
        .exceptionHandling(withDefaults())        // → ExceptionTranslationFilter
        .headers(withDefaults())                  // → HeaderWriterFilter
        .sessionManagement(withDefaults())        // → SessionManagementFilter
        .securityContext(withDefaults())          // → SecurityContextHolderFilter
        .requestCache(withDefaults())             // → RequestCacheAwareFilter
        .anonymous(withDefaults())                // → AnonymousAuthenticationFilter
        .servletApi(withDefaults())               // → SecurityContextHolderAwareRequestFilter
        .apply(new DefaultLoginPageConfigurer<>()); // → DefaultLoginPageGeneratingFilter

    http.logout(withDefaults());                  // → LogoutFilter

    // ⑥ 应用 CORS 配置(如果容器中存在 CorsConfigurationSource Bean)
    applyCorsIfAvailable(http);
    // ⑦ 通过 SpringFactoriesLoader 加载 META-INF/spring.factories 中的 AbstractHttpConfigurer
    applyDefaultConfigurers(http);
    return http;
}

关键洞察:

  • prototype 作用域意味着每次注入都会创建新实例,这允许你在同一个应用中配置多条不同的安全过滤器链
  • 默认配置中显式开启了 10 个安全功能(加上条件加载的 CORS 和 SPI 扩展),这就是为什么 Spring Boot 项目一启动就有 CSRF、表单登录、HTTP Basic 等全套防护,你什么都不配也有一个能用的安全体系

第二,提供组装核心依赖。 为了构建一个功能完备的 HttpSecurity 对象,该配置类负责准备并注入以下关键组件:

核心依赖 作用
AuthenticationManager 核心认证管理器,被登录过滤器调用
AuthenticationManagerBuilder 认证管理器的构建器
ObjectPostProcessor 对象后处理器:在 Bean 创建后进行生命周期管理和增强
LazyPasswordEncoder 延迟加载的密码编码器,避免循环依赖

1.2 HttpSecurity 的核心数据结构

java 复制代码
public final class HttpSecurity extends 
        AbstractConfiguredSecurityBuilder<DefaultSecurityFilterChain, HttpSecurity>
        implements SecurityBuilder<DefaultSecurityFilterChain>, 
                   HttpSecurityBuilder<HttpSecurity> {

    private final List<OrderedFilter> filters = new ArrayList<>();  // 未排序的过滤器列表
    private RequestMatcher requestMatcher = AnyRequestMatcher.INSTANCE;  // 默认匹配所有请求
    private AuthenticationManager authenticationManager;  // 认证管理器
}

注意filters 列表中的元素类型是 OrderedFilter(包含 Filter + order 值),而非裸 Filter。这个 order 值就是 FilterOrderRegistration 中定义的序号。OrderedFilterHttpSecurity 的私有内部类,实现了 OrderedFilter 接口。

1.3 Configurer 的注册机制:getOrApply → apply → add

当我们调用 http.formLogin() 时,实际执行的是一个三段式调用链。

阶段一:http.xxx() 方法(以 csrf 为例):

java 复制代码
// HttpSecurity.csrf 方法
public HttpSecurity csrf(Customizer<CsrfConfigurer<HttpSecurity>> csrfCustomizer) throws Exception {
    ApplicationContext context = getContext();
    // ① getOrApply:获取或创建 CsrfConfigurer(确保单例)
    // ② customize:应用你的自定义配置(Lambda 表达式)
    csrfCustomizer.customize(getOrApply(new CsrfConfigurer<>(context)));
    return HttpSecurity.this;  // 链式调用
}

阶段二:getOrApplyapply

java 复制代码
private <C extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity>> C 
        getOrApply(C configurer) throws Exception {
    // 先去检查是否已经注册过同类型的 Configurer
    C existingConfig = (C) getConfigurer(configurer.getClass());
    if (existingConfig != null) {
        return existingConfig;  // 已存在,直接返回(确保单例)
    }
    return apply(configurer);   // 不存在,注册新的
}

public <C extends SecurityConfigurerAdapter<O, B>> C apply(C configurer) throws Exception {
    configurer.addObjectPostProcessor(this.objectPostProcessor);  // 注入 ObjectPostProcessor
    configurer.setBuilder((B) this);   // 设置 Builder 引用(双向关联)
    add(configurer);                   // 加入内部列表
    return configurer;
}

apply 方法做了三件关键的事:

  1. addObjectPostProcessor:将 HttpSecurity 持有的 ObjectPostProcessor 注入到 Configurer 中。Configurer 在后续创建 Filter 时,需要用这个处理器确保 Filter 也受 Spring 容器管理。
  2. setBuilder((B) this):建立双向关联------Configurer 内部可以通过 getBuilder() 拿到 HttpSecurity,用于获取共享对象(如 AuthenticationManager)。
  3. add(configurer):将 Configurer 注册到内部的 configurers Map 中。如果没有这一步,http.build() 时这个 Configurer 就不会被遍历到,对应的 Filter 也不会被创建。

阶段三:add 方法------记录到 LinkedHashMap:

java 复制代码
// AbstractConfiguredSecurityBuilder 内部
private final LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, 
    List<SecurityConfigurer<O, B>>> configurers = new LinkedHashMap<>();

private <C extends SecurityConfigurer<O, B>> void add(C configurer) {
    Assert.notNull(configurer, "configurer cannot be null");
    Class<? extends SecurityConfigurer<O, B>> clazz = 
        (Class<? extends SecurityConfigurer<O, B>>) configurer.getClass();
    synchronized (this.configurers) {
        if (this.buildState.isConfigured()) {
            throw new IllegalStateException(
                "Cannot apply " + configurer + " to already built object");
        }
        // 从 Map 中取出该类型的列表(如果不存在则创建)
        List<SecurityConfigurer<O, B>> configs = 
            this.configurers.getOrDefault(clazz, new ArrayList<>(1));
        configs.add(configurer);
        this.configurers.put(clazz, configs);
    }
}

设计要点:

  • LinkedHashMap → 保持 Configurer 的插入顺序
  • Key 是 Configurer 的 Class 类型 → 同类型的 Configurer 会被归入同一列表(getOrApply 负责去重)
  • synchronized → 线程安全,防止并发构建

1.4 with() 方法:apply() 的现代化替代

从 Spring Security 6.x 开始,推荐使用 with() 方法来注册自定义 Configurer:

java 复制代码
public <C extends SecurityConfigurerAdapter<O, B>> B with(
        C configurer, Customizer<C> customizer) throws Exception {
    configurer.addObjectPostProcessor(this.objectPostProcessor);
    configurer.setBuilder((B) this);
    add(configurer);                    // 先注册
    customizer.customize(configurer);   // 后配置
    return (B) this;
}

澄清 :在 Spring Security 6.4.x 中,apply() 方法本身并未 被标记为 @Deprecated。被标记废弃的是各个无参配置方法(如 csrf(), formLogin() 等),推荐使用 Lambda DSL 替代。with() 方法是新增的便捷 API,它将 customizer.customize() 放在 add() 之后执行------这意味着自定义配置一定是在 Configurer 已经完成注册和基础设置后才应用的,对于自定义 Configurer 的注册,apply() 仍然可用且有效。

1.5 Configurer → Filter 映射表

你写的配置 对应的 Configurer 最终产出的 Filter
http.formLogin() FormLoginConfigurer UsernamePasswordAuthenticationFilter
http.httpBasic() HttpBasicConfigurer BasicAuthenticationFilter
http.csrf() CsrfConfigurer CsrfFilter
http.cors() CorsConfigurer CorsFilter
http.authorizeHttpRequests() AuthorizeHttpRequestsConfigurer AuthorizationFilter
http.sessionManagement() SessionManagementConfigurer SessionManagementFilter
http.logout() LogoutConfigurer LogoutFilter
http.anonymous() AnonymousConfigurer AnonymousAuthenticationFilter
http.rememberMe() RememberMeConfigurer RememberMeAuthenticationFilter
http.headers() HeadersConfigurer HeaderWriterFilter
http.exceptionHandling() ExceptionHandlingConfigurer ExceptionTranslationFilter

二、构建过程:doBuild 三步曲完整拆解

2.0 触发入口:SecurityBuilder.build()

一切始于 SecurityBuilder.build() 方法:

java 复制代码
// SecurityBuilder 接口
public interface SecurityBuilder<O> {
    O build() throws Exception;
}

// AbstractSecurityBuilder 抽象类
public abstract class AbstractSecurityBuilder<O> implements SecurityBuilder<O> {
    private AtomicBoolean building = new AtomicBoolean();
    private O object;

    @Override
    public final O build() throws Exception {
        if (this.building.compareAndSet(false, true)) {
            this.object = doBuild();   // ★ 委托给子类的 doBuild
            return this.object;
        }
        throw new AlreadyBuiltException("This object has already been built");
    }

    protected abstract O doBuild() throws Exception;
}

关键设计:

  • AtomicBoolean 保证同一个 Builder 只能被 build() 一次(防重复构建)
  • 模板方法模式:build() 是 final 的,子类只能实现 doBuild()

2.1 doBuild 三步曲

AbstractConfiguredSecurityBuilder.doBuild 是三者的核心:

java 复制代码
@Override
protected final O doBuild() throws Exception {
    synchronized (this.configurers) {
        this.buildState = BuildState.INITIALIZING;
        beforeInit();
        init();                         // ★ 第一步:初始化

        this.buildState = BuildState.CONFIGURING;
        beforeConfigure();
        configure();                    // ★ 第二步:配置(创建 Filter)

        this.buildState = BuildState.BUILDING;
        O result = performBuild();      // ★ 第三步:构建(排序+组装)
        this.buildState = BuildState.BUILT;
        return result;
    }
}

synchronized (this.configurers) --- 整个构建过程是同步的,因为 configurers 这个 LinkedHashMap 在 init/configure 阶段会被遍历和修改,必须保证线程安全。

2.2 第一步:init() ------ 初始化阶段

java 复制代码
private void init() throws Exception {
    Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
    for (SecurityConfigurer<O, B> configurer : configurers) {
        configurer.init((B) this);
    }
}

init 阶段的具体任务(以几个典型 Configurer 为例):

Configurer init() 做了什么
FormLoginConfigurer 确认登录页面 URL、初始化认证相关的默认 Filter
CsrfConfigurer 初始化 CSRF Token 仓库(默认 HttpSessionCsrfTokenRepository)
LogoutConfigurer 初始化登出处理器、登出成功处理器
SessionManagementConfigurer 初始化会话认证策略(SessionAuthenticationStrategy)

2.3 第二步:configure() ------ 创建 Filter 阶段(★ 最关键)

java 复制代码
private void configure() throws Exception {
    Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
    for (SecurityConfigurer<O, B> configurer : configurers) {
        configurer.configure((B) this);
    }
}

这是整个构建过程中最关键的一步:每个 Configurer 在此创建自己负责的 Filter,并调用 http.addFilter(filter) 添加到 HttpSecurity 的过滤器列表。

完整实例一:CsrfConfigurer.configure()

以 CSRF 为例,看一个 Configurer 是如何变成 Filter 的:

java 复制代码
// CsrfConfigurer 的 configure 方法
@Override
public void configure(H http) {
    // ① 创建 CsrfFilter
    CsrfFilter filter = new CsrfFilter(this.csrfTokenRepository);

    // ② 设置需要 CSRF 保护的请求匹配器(默认匹配所有"危险"方法:POST/PUT/DELETE)
    RequestMatcher requireCsrfProtectionMatcher = getRequireCsrfProtectionMatcher();
    if (requireCsrfProtectionMatcher != null) {
        filter.setRequireCsrfProtectionMatcher(requireCsrfProtectionMatcher);
    }

    // ③ 设置拒绝访问处理器(CSRF Token 不匹配时如何处理)
    AccessDeniedHandler accessDeniedHandler = createAccessDeniedHandler(http);
    if (accessDeniedHandler != null) {
        filter.setAccessDeniedHandler(accessDeniedHandler);
    }

    // ④ 将 CSRF Token 清除处理器注册到登出流程
    LogoutConfigurer<H> logoutConfigurer = http.getConfigurer(LogoutConfigurer.class);
    if (logoutConfigurer != null) {
        logoutConfigurer.addLogoutHandler(
            new CsrfLogoutHandler(this.csrfTokenRepository));
    }

    // ⑤ 将 CSRF 会话认证策略注册到会话管理流程
    SessionManagementConfigurer<H> sessionConfigurer = 
        http.getConfigurer(SessionManagementConfigurer.class);
    if (sessionConfigurer != null) {
        sessionConfigurer.addSessionAuthenticationStrategy(
            getSessionAuthenticationStrategy());
    }

    // ⑥ 后置处理(如依赖注入)
    filter = postProcess(filter);

    // ⑦ 核心:将创建好的 CsrfFilter 添加到 HttpSecurity 的过滤器列表
    http.addFilter(filter);
}

这个方法的精髓在于:它不仅创建了一个 CsrfFilter,还通过 http.getConfigurer() 拿到了其他 Configurer(如 LogoutConfigurer、SessionManagementConfigurer),并向它们注册了 CSRF 相关的处理器。Configurer 之间可以通过 HttpSecurity 这个"共享上下文"相互协作。

完整实例二:AuthorizeHttpRequestsConfigurer.configure()

java 复制代码
@Override
public void configure(H http) {
    // ① 从注册表创建授权管理器(合并所有 URL 映射规则)
    AuthorizationManager<HttpServletRequest> authorizationManager = 
        this.registry.createAuthorizationManager();

    // ② 创建 AuthorizationFilter
    AuthorizationFilter authorizationFilter = 
        new AuthorizationFilter(authorizationManager);

    // ③ 设置授权事件发布器
    authorizationFilter.setAuthorizationEventPublisher(this.publisher);

    // ④ 设置 SecurityContext 持有者策略
    authorizationFilter.setSecurityContextHolderStrategy(
        getSecurityContextHolderStrategy());

    // ⑤ 添加到过滤器链
    http.addFilter(postProcess(authorizationFilter));
}

2.4 第三步:performBuild() ------ 排序 + 组装

到了这一步,所有 Filter 都已经在 filters 列表中了。performBuild() 只需要做最后一件事------排序并包装:

java 复制代码
// HttpSecurity.performBuild
@Override
protected DefaultSecurityFilterChain performBuild() {
    // 校验:authorizeHttpRequests 和 authorizeRequests 不能同时使用
    ExpressionUrlAuthorizationConfigurer<?> expressionConfigurer = 
        getConfigurer(ExpressionUrlAuthorizationConfigurer.class);
    AuthorizeHttpRequestsConfigurer<?> httpConfigurer = 
        getConfigurer(AuthorizeHttpRequestsConfigurer.class);
    boolean oneConfigurerPresent = expressionConfigurer == null ^ httpConfigurer == null;
    Assert.state((expressionConfigurer == null && httpConfigurer == null) || oneConfigurerPresent,
        "authorizeHttpRequests cannot be used in conjunction with authorizeRequests. Please select just one.");

    // 排序:按照 FilterOrderRegistration 中定义的序号
    this.filters.sort(OrderComparator.INSTANCE);

    // 剥离 OrderedFilter 包装,只取裸 Filter
    List<Filter> sortedFilters = new ArrayList<>(this.filters.size());
    for (OrderedFilter orderedFilter : this.filters) {
        sortedFilters.add(orderedFilter.filter);
    }

    // 构建并返回
    return new DefaultSecurityFilterChain(this.requestMatcher, sortedFilters);
}

OrderComparator.INSTANCE 是如何排序的? 由于 OrderedFilter 实现了 Ordered 接口,OrderComparator 会直接调用其 getOrder() 方法获取顺序值,然后按升序排列。而这个顺序值,正是在 Filter 被 http.addFilter() 时,由 FilterOrderRegistration 中定义的序号注入的。

2.5 构建产物:DefaultSecurityFilterChain

java 复制代码
public final class DefaultSecurityFilterChain implements SecurityFilterChain {
    private final RequestMatcher requestMatcher;  // 匹配哪些请求(例如 /api/**)
    private final List<Filter> filters;           // 已排序的过滤器列表

    @Override
    public boolean matches(HttpServletRequest request) {
        return this.requestMatcher.matches(request);
    }

    @Override
    public List<Filter> getFilters() {
        return this.filters;
    }
}

三、三个 performBuild 的完整实现对比

整个 Spring Security 的构建体系分为三层,每一层都有自己的 performBuild 实现。理解这三者的区别,是贯通整个框架架构的关键。

3.1 底层:AuthenticationManagerBuilder.performBuild()

java 复制代码
@Override
protected ProviderManager performBuild() throws Exception {
    if (!isConfigured()) {
        return null;  // 没有任何 AuthenticationProvider 配置
    }
    // ① 创建 ProviderManager,注入所有收集到的 AuthenticationProvider
    ProviderManager providerManager = new ProviderManager(
        this.authenticationProviders,       // 所有注册的 Provider
        this.parentAuthenticationManager    // 父级认证管理器(可选)
    );
    // ② 设置配置
    if (this.eraseCredentials != null) {
        providerManager.setEraseCredentialsAfterAuthentication(this.eraseCredentials);
    }
    if (this.eventPublisher != null) {
        providerManager.setAuthenticationEventPublisher(this.eventPublisher);
    }
    // ③ 后置处理
    providerManager = postProcess(providerManager);
    return providerManager;
}
  • 产物ProviderManager(包含一组 AuthenticationProvider 的责任链)

3.2 中间层:HttpSecurity.performBuild()

java 复制代码
@Override
protected DefaultSecurityFilterChain performBuild() {
    // ① 排序
    this.filters.sort(OrderComparator.INSTANCE);
    // ② 剥离包装
    List<Filter> sortedFilters = new ArrayList<>(this.filters.size());
    for (OrderedFilter orderedFilter : this.filters) {
        sortedFilters.add(orderedFilter.filter);
    }
    // ③ 构建
    return new DefaultSecurityFilterChain(this.requestMatcher, sortedFilters);
}
  • 产物DefaultSecurityFilterChain(一条规则 + 一组已排序的过滤器)

3.3 顶层:WebSecurity.performBuild()

WebSecurity 是 Spring Security 在 Servlet 容器中的总入口。它的 performBuild 是最终构建 FilterChainProxy 的地方------这是整个 Spring Security 的"最终产品"。

注意 :以下 springSecurityFilterChain Bean 定义位于 WebSecurityConfiguration 类中,而非 WebSecurity 类中。WebSecurity.performBuild() 是被该 Bean 定义调用的。

java 复制代码
// WebSecurityConfiguration 中的 Bean 注册
@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
public Filter springSecurityFilterChain(ObjectProvider<HttpSecurity> provider) throws Exception {
    boolean hasFilterChain = !this.securityFilterChains.isEmpty();
    // 如果用户没有定义任何 SecurityFilterChain,自动创建一个默认的
    if (!hasFilterChain) {
        this.webSecurity.addSecurityFilterChainBuilder(() -> {
            HttpSecurity httpSecurity = provider.getObject();
            httpSecurity.authorizeHttpRequests(authorize -> 
                authorize.anyRequest().authenticated());
            httpSecurity.formLogin(Customizer.withDefaults());
            httpSecurity.httpBasic(Customizer.withDefaults());
            return httpSecurity.build();
        });
    }
    // 注册用户自定义的 SecurityFilterChain
    for (SecurityFilterChain securityFilterChain : this.securityFilterChains) { 
        this.webSecurity.addSecurityFilterChainBuilder(() -> securityFilterChain);
    }
    // 应用用户自定义的 WebSecurityCustomizer
    for (WebSecurityCustomizer customizer : this.webSecurityCustomizers) {
        customizer.customize(this.webSecurity);
    }
    // 最终构建 FilterChainProxy
    return this.webSecurity.build();
}
// WebSecurity.performBuild
@Override
protected Filter performBuild() throws Exception {
    // ① 收集所有 SecurityFilterChain
    int chainSize = this.ignoredRequests.size() + this.securityFilterChainBuilders.size();
    List<SecurityFilterChain> securityFilterChains = new ArrayList<>(chainSize);

    // 处理 ignored requests(已废弃,推荐用 permitAll)
    for (RequestMatcher ignoredRequest : this.ignoredRequests) {
        SecurityFilterChain securityFilterChain = new DefaultSecurityFilterChain(ignoredRequest);
        securityFilterChains.add(securityFilterChain);
    }

    // 调用每个 SecurityBuilder.build() 构建 SecurityFilterChain
    for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : 
            this.securityFilterChainBuilders) {
        SecurityFilterChain securityFilterChain = securityFilterChainBuilder.build();
        securityFilterChains.add(securityFilterChain);
    }

    // ② 创建 FilterChainProxy
    FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);

    // ③ 配置防火墙
    if (this.httpFirewall != null) {
        filterChainProxy.setFirewall(this.httpFirewall);
    }

    // ④ 配置请求拒绝处理器
    if (this.requestRejectedHandler != null) {
        filterChainProxy.setRequestRejectedHandler(this.requestRejectedHandler);
    }

    // ⑤ 后置处理
    filterChainProxy.afterPropertiesSet();

    // ⑥ 如果开启 debug,包装一层 DebugFilter
    Filter result = filterChainProxy;
    if (this.debugEnabled) {
        result = new DebugFilter(filterChainProxy);
    }

    return result;
}
  • 产物FilterChainProxy(实现了 jakarta.servlet.Filter 接口,作为 Spring Security 在 Servlet 容器中的唯一代表)

3.4 三层体系的职责对照

层级 产物 一句话
底层 AuthenticationManagerBuilder ProviderManager 构建认证核心------"谁能登录"
中间层 HttpSecurity DefaultSecurityFilterChain 构建安全规则------"这条链有什么规则"
顶层 WebSecurity FilterChainProxy 构建总代理------"所有链的统一入口"

四、多过滤器链的路由机制(附完整示例)

一个应用可以配置多条 SecurityFilterChain,FilterChainProxy 会按 @Order 顺序逐一匹配:

java 复制代码
@Bean
@Order(1)  // ★ 优先级最高,先被检查
public SecurityFilterChain apiSecurityFilterChain(HttpSecurity http) throws Exception {
    http
        .securityMatcher("/api/**")    // 匹配规则:/api/** 的请求走这条链
        .csrf(csrf -> csrf.disable())
        .sessionManagement(s -> s.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
        .addFilterBefore(jwtFilter(), UsernamePasswordAuthenticationFilter.class)
        .authorizeHttpRequests(auth -> auth.anyRequest().authenticated());
    return http.build();
}

@Bean
@Order(2)  // ★ 优先级次之,链1不匹配时才检查
public SecurityFilterChain webSecurityFilterChain(HttpSecurity http) throws Exception {
    http
        .securityMatcher("/**")        // 兜底规则:匹配所有请求
        .formLogin(form -> form.loginPage("/login"))
        .authorizeHttpRequests(auth -> auth.anyRequest().authenticated());
    return http.build();
}

路由规则:

  • 请求 /api/users → 链1检查 → securityMatcher("/api/**") 匹配 ✅ → JWT 认证
  • 请求 /dashboard → 链1检查 → securityMatcher("/api/**") 不匹配 ❌ → 链2检查 → securityMatcher("/**") 匹配 ✅ → 表单认证

@Order 决定检查顺序,securityMatcher 决定匹配条件;第一个匹配的链执行,后续链不再检查

⚠️ 注意@OrdersecurityMatcher 是两个独立的维度。@Order 决定哪条链先被检查,securityMatcher 决定该请求是否属于这条链。

五、完整构建流程图

六、总结

本文从顶层到底层完整拆解了 Spring Security 的构建体系。三个核心认知:

认知层次 核心知识点 一句话
Builder 模式 doBuild 三步曲 init(初始化依赖)→ configure(创建 Filter)→ performBuild(排序组装)
Configurer 机制 http.xxx() → Configurer → Filter 每个安全功能的配置背后都有一个 Configurer,在 configure 阶段创建并注册 Filter
三层构建体系 AuthMgrBuilder → HttpSecurity → WebSecurity 从认证核心到安全链再到总入口,层层递进

最重要的概念:getOrApply + apply + add + doBuild 是 Spring Security 配置体系的"骨架"。理解了这个机制:

  • 你就知道为什么 http.formLogin() 会生成 UsernamePasswordAuthenticationFilter
  • 你就知道自定义过滤器为什么用 addFilterBefore 而非直接 addFilter
  • 你就知道为什么同一个 Configurer 只能配置一次(getOrApply 的去重逻辑)
串联知识点 说明
HttpSecurity 创建 HttpSecurityConfiguration,prototype 作用域,默认开启 10 个安全功能 + 条件加载
Configurer 注册 getOrApply → apply → add(去重 + ObjectPostProcessor注入 + 双向关联)
doBuild 三步曲 init() → configure() → performBuild()
三个 performBuild ProviderManager / DefaultSecurityFilterChain / FilterChainProxy
FilterChainProxy 总入口:多链路由 + 防火墙 + 虚拟链驱动

下一篇预告:将深入分析 Spring Security 的总入口 ------ FilterChainProxy.doFilter() 的完整实现------多链路由匹配(getFilters)、请求防火墙校验(HttpFirewall)、虚拟过滤器链驱动(VirtualFilterChain)、以及异常处理与 SecurityContext 清理的完整源码。

相关推荐
自进化Agent智能体1 小时前
Hermes 自进化Skill:让AI能力自己长出来
后端
宇宙之一粟3 小时前
如何判断是时候离开了
后端·程序员
武子康3 小时前
Java-16 深入浅出MyBatis 架构设计与源码剖析:从初始化到 SQL 执行全流程
java·后端
逍遥运德3 小时前
Java编程高频的“技术点”-03:“下划线命名”参数,后端用"驼峰命名"接收
java·后端·架构
To_OC3 小时前
阿里云多模态图片生成!抛弃SDK手写Fetch请求,我终于搞懂了大模型调用底层
javascript·后端·aigc
西安邮电大学4 小时前
binlog/redolog/undolog三者对比
java·后端·其他·面试
码客日记4 小时前
Spring Boot 全局跨域配置与前后端联调避坑
java·spring boot·后端
fox_lht5 小时前
14.3.重构
开发语言·后端·rust