Spring Security 默认配置源码分析

在 Spring Security 的官方文档中,说明了,它的配置为以下的默认实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
// 开启授权保护
.authorizeRequests(authorize -> authorize
// 所有请求开启授权保护
.anyRequest()
// 已经认证的请求会被自动授权
.authenticated()
)
// 自动使用表单的授权方式,也就是自动生成的登入表单和登出表单
.formLogin(withDefaults())
// 使用基本授权方式
.httpBasic(withDefaults());
return http.build();
}

说明一下什么是使用表单的授权方式,什么是使用基本授权方式,注意这里的授权其实是认证))

表单认证是基于页面表单的用户身份验证方式,适用于传统 Web 应用(有前端页面的场景)。默认行为就会自动生成一个简单的登录页面(包含用户名输入框、密码输入框、提交按钮)。

可以按照下面这样自定义配置:

1
2
3
4
5
6
7
8
http.formLogin(form -> form
.loginPage("/custom-login") // 自定义登录页路径
.loginProcessingUrl("/do-login") // 处理登录请求的路径(默认/login)
.defaultSuccessUrl("/home", true) // 登录成功后跳转的页面
.failureUrl("/custom-login?error=true") // 登录失败后跳转的页面
.usernameParameter("user") // 自定义表单中用户名的参数名(默认username)
.passwordParameter("pass") // 自定义表单中密码的参数名(默认password)
);

HTTP Basic 认证就是基本认证,是基于 HTTP 协议规范的认证方式,适用于 API 接口(无前端页面的场景),通过请求头传递认证信息。当未认证用户访问受保护资源时,服务器不会重定向到登录页,而是返回 401 Unauthorized 响应,并在响应头中添加 WWW-Authenticate: Basic realm="Realm"

客户端(如浏览器、Postman、前端 Ajax)收到响应后,会弹出一个系统级别的登录弹窗(由浏览器 / 客户端生成,非自定义页面)。认证信息通过 HTTP 头传递(Base64 编码,不加密,生产环境需配合 HTTPS 使用)。无默认登出机制(需手动清除请求头或关闭客户端)。

而默认的15(16)个过滤器就是这些,好像我注销了一个配置,就有一个没装上

image-20250921212452057

在 Spring Security 中,这些默认的过滤器在不同的配置器(Configurer)中被创建和配置,然后由HttpSecurity进行统一管理和组装,最终形成DefaultSecurityFilterChain 。关于这个的内容下面会细说

Spring Security 的自动配置

HttpSecurity 中加载了什么

之前我们讲解的初始化配置只是在很简单的层面上做了一些介绍,方便后面的学习,在这里我们将有条理的分析整个Spring Security 自动配置

HttpSecurity 是 Spring Security 中配置 HTTP 安全规则的核心入口类

HttpSecurity 这个 bean 算是最基础的底层配置了,它是Spring Security各种配置的基础,这个bean是Spring Security自动加载的,并且同时会默认帮我们完成了Spring Security完整功能的各项配置,我们先找到这个bean加载的位置:

HttpSecurityConfiguration类中,加载了HttpSecurity

image-20250921205517962
image-20250921210339727

Spring Security 对 Web 应用的保护,本质是对 “HTTP 请求” 的拦截与规则校验 HttpSecurity 就是负责定义这些 “拦截规则” 的核心类,包括:

  • 哪些请求需要认证?

    AuthorizeHttpRequestsConfigurer实现,AuthorizeHttpRequestsConfigurer是处理请求授权的核心配置器,其内部的AuthorizationManagerRequestMatcherRegistry提供了requestMatchers()permitAll()authenticated()等方法,用于定义 “哪些请求需要认证”。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // HttpSecurity 中与请求授权相关的方法
    public HttpSecurity authorizeHttpRequests(Customizer<AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry> authorizeHttpRequestsCustomizer) throws Exception {
    ApplicationContext context = this.getContext();

    // 获取或创建 AuthorizeHttpRequestsConfigurer 配置器
    AuthorizeHttpRequestsConfigurer<HttpSecurity> configurer = (AuthorizeHttpRequestsConfigurer) this.getOrApply(new AuthorizeHttpRequestsConfigurer(context));

    // 通过 Customizer 自定义授权规则(如哪些路径允许匿名、哪些需要认证)
    authorizeHttpRequestsCustomizer.customize(configurer.getRegistry());
    return this;
    }

    实际开发中,我们通过链式调用配置规则,例如

    1
    2
    3
    4
    5
    http.authorizeHttpRequests(auth -> auth
    .requestMatchers("/public/**").permitAll() // 公开路径,无需认证
    .requestMatchers("/admin/**").hasRole("ADMIN") // 管理员角色可访问
    .anyRequest().authenticated() // 其他请求必须认证
    );
    • 这些配置最终会转换为Filter(如FilterSecurityInterceptor),在请求到达时拦截并校验权限。
  • 用什么方式认证(表单登录、HTTP Basic、OAuth2 等)?

    HttpSecurity通过不同的配置器支持多种认证方式,以下是最常用的几种:

    • 表单登录(formLogin

      1
      2
      3
      4
      5
      6
      7
      8
      // 表单登录配置方法
      public HttpSecurity formLogin(Customizer<FormLoginConfigurer<HttpSecurity>> formLoginCustomizer) throws Exception {
      // 获取或创建 FormLoginConfigurer 配置器(负责表单登录逻辑)
      FormLoginConfigurer<HttpSecurity> configurer = (FormLoginConfigurer) this.getOrApply(new FormLoginConfigurer());
      // 自定义表单登录配置(如登录页URL、成功跳转页等)
      formLoginCustomizer.customize(configurer);
      return this;
      }
      • FormLoginConfigurer是处理表单登录的配置器,负责:
        • 生成默认登录页(或指定自定义登录页loginPage("/login"));
        • 处理登录请求(默认/login)、成功 / 失败跳转;
        • 生成UsernamePasswordAuthenticationFilter过滤器,拦截登录请求并校验用户名密码。
    • HTTP Basic 认证

      1
      2
      3
      4
      5
      6
      7
      8
      // HTTP Basic 认证配置方法
      public HttpSecurity httpBasic(Customizer<HttpBasicConfigurer<HttpSecurity>> httpBasicCustomizer) throws Exception {
      // 获取或创建 HttpBasicConfigurer 配置器(负责HTTP Basic认证)
      HttpBasicConfigurer<HttpSecurity> configurer = (HttpBasicConfigurer) this.getOrApply(new HttpBasicConfigurer());
      // 自定义HTTP Basic配置(如 Realm 名称)
      httpBasicCustomizer.customize(configurer);
      return this;
      }
    • OAuth2 登录

      1
      2
      3
      4
      5
      6
      // OAuth2 登录配置方法(第三方登录,如GitHub、微信)
      public HttpSecurity oauth2Login(Customizer<OAuth2LoginConfigurer<HttpSecurity>> oauth2LoginCustomizer) throws Exception {
      OAuth2LoginConfigurer<HttpSecurity> configurer = (OAuth2LoginConfigurer) this.getOrApply(new OAuth2LoginConfigurer());
      oauth2LoginCustomizer.customize(configurer);
      return this;
      }
      • OAuth2LoginConfigurer用于配置 OAuth2/OpenID Connect 登录,支持第三方认证;
      • 会生成OAuth2LoginAuthenticationFilter,处理 OAuth2 回调请求并完成认证。
  • 认证后如何授权(基于角色、权限)?

    授权逻辑与 “哪些请求需要认证” 同属一个配置器,核心是通过AuthorizationManager校验用户权限是否匹配请求要求。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    // AuthorizeHttpRequestsConfigurer 内部类,用于定义授权规则
    public class AuthorizationManagerRequestMatcherRegistry {
    // 例如:配置某个路径需要特定角色
    public AuthorizationManagerRequestMatcherRegistry hasRole(String role) {
    // 内部会创建基于角色的 AuthorizationManager
    return this.access(this.roleHierarchyAuthoritiesMapper(role));
    }

    // 配置某个路径允许匿名访问
    public AuthorizationManagerRequestMatcherRegistry permitAll() {
    return this.access(AuthenticatedAuthorizationManager.permitAll());
    }

    // 配置其他请求必须认证
    public AuthorizationManagerRequestMatcherRegistry authenticated() {
    return this.access(AuthenticatedAuthorizationManager.authenticated());
    }
    }
    • 授权规则最终会转换为AuthorizationManager(权限管理器),在请求拦截时校验:
      • 用户是否拥有hasRole("ADMIN")要求的角色;
      • 请求是否符合permitAll()(无需认证)或authenticated()(必须认证)。
    • 底层由FilterSecurityInterceptor过滤器执行校验,不通过则抛出AccessDeniedException
  • 如何处理跨站请求伪造(CSRF)、会话、异常?

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // CSRF 防护配置方法
    public HttpSecurity csrf(Customizer<CsrfConfigurer<HttpSecurity>> csrfCustomizer) throws Exception {
    ApplicationContext context = this.getContext();
    // 获取或创建 CsrfConfigurer 配置器(负责CSRF防护)
    CsrfConfigurer<HttpSecurity> configurer = (CsrfConfigurer) this.getOrApply(new CsrfConfigurer(context));
    // 自定义CSRF配置(如开启/关闭、令牌存储方式)
    csrfCustomizer.customize(configurer);
    return this;
    }
  • 如何处理会话?

    sessionManagement配置

    1
    2
    3
    4
    5
    6
    7
    8
    // 会话管理配置方法
    public HttpSecurity sessionManagement(Customizer<SessionManagementConfigurer<HttpSecurity>> sessionManagementCustomizer) throws Exception {
    // 获取或创建 SessionManagementConfigurer 配置器(负责会话管理)
    SessionManagementConfigurer<HttpSecurity> configurer = (SessionManagementConfigurer) this.getOrApply(new SessionManagementConfigurer());
    // 自定义会话配置(如会话创建策略、超时时间)
    sessionManagementCustomizer.customize(configurer);
    return this;
    }
  • 如何处理异常

    exceptionHandling配置

    1
    2
    3
    4
    5
    6
    7
    8
    // 异常处理配置方法
    public HttpSecurity exceptionHandling(Customizer<ExceptionHandlingConfigurer<HttpSecurity>> exceptionHandlingCustomizer) throws Exception {
    // 获取或创建 ExceptionHandlingConfigurer 配置器(负责异常处理)
    ExceptionHandlingConfigurer<HttpSecurity> configurer = (ExceptionHandlingConfigurer) this.getOrApply(new ExceptionHandlingConfigurer());
    // 自定义异常处理(如认证失败、授权失败的响应)
    exceptionHandlingCustomizer.customize(configurer);
    return this;
    }
    • ExceptionHandlingConfigurer用于配置认证 / 授权过程中的异常处理,例如:
      • 认证失败(authenticationEntryPoint):配置未认证用户访问受保护资源时的响应(如跳转登录页或返回 401 JSON);
      • 授权失败(accessDeniedHandler):配置已认证用户权限不足时的响应(如跳转拒绝页或返回 403 JSON)。
    • 会生成ExceptionTranslationFilter过滤器,捕获并处理认证 / 授权异常。

所以说,HttpSecurity 基于建造者模式设计,同时通过 配置器(Configurer)对不同安全模块进行解耦,上面说的都是配置器,每个配置器负责一个细分领域的配置

然后这些配置器最终会生成对应的Filter(过滤器),由HttpSecurity统一组装为DefaultSecurityFilterChain,在 HTTP 请求到达时按顺序拦截并应用规则,实现对 Web 应用的安全保护。

设计元素 作用
建造者(Builder) HttpSecurity 本身实现 SecurityBuilder,负责最终构建 DefaultSecurityFilterChain
配置器(Configurer) 每个配置器对应一个安全模块(如 FormLoginConfigurer 负责表单登录,CsrfConfigurer 负责 CSRF 防护),通过 HttpSecurityxxxConfigurer() 方法接入

然后,HttpSecurity 通过不同的配置器,覆盖了 Web 安全的全场景需求,以下是最常用的配置器:

配置器方法 对应配置器类 核心能力
formLogin() FormLoginConfigurer 配置表单登录(自定义登录页、成功跳转、失败处理等)
authorizeHttpRequests() AuthorizeHttpRequestsConfigurer 配置请求授权规则(基于角色、权限、IP 等控制访问)
csrf() CsrfConfigurer 配置CSRF 防护(开启 / 关闭、自定义令牌等)
logout() LogoutConfigurer 配置登出逻辑(登出 URL、成功跳转、清除会话等)
sessionManagement() SessionManagementConfigurer 配置会话管理(会话创建策略、超时时间、并发控制等)
httpBasic() HttpBasicConfigurer 配置HTTP Basic 认证(适合 API 场景)
oauth2Login() OAuth2LoginConfigurer 配置OAuth2 登录(第三方登录,如 GitHub、微信)
exceptionHandling() ExceptionHandlingConfigurer 配置异常处理(认证失败、授权失败的页面或 JSON 响应)

HttpSecurity自动装配的流程

我们一点一点来看看,Spring Security 都帮我们怎么进行了 HttpSecurity 的默认配置了

image-20250921213035815
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// HttpSecurityConfiguration 中创建 HttpSecurity 的核心方法
@Bean
@Scope("prototype")
public HttpSecurity httpSecurity() throws Exception {
// 1. 初始化密码编码器(懒加载,自动适配环境中的 PasswordEncoder)
LazyPasswordEncoder passwordEncoder = new LazyPasswordEncoder(this.context);
// 2. 初始化 AuthenticationManagerBuilder(用于构建 AuthenticationManager)
AuthenticationManagerBuilder authenticationBuilder =
new DefaultPasswordEncoderAuthenticationManagerBuilder(this.objectPostProcessor, passwordEncoder);
authenticationBuilder.parentAuthenticationManager(this.authenticationManager());
authenticationBuilder.authenticationEventPublisher(this.getAuthenticationEventPublisher());
// 3. 创建 HttpSecurity 实例,并传入核心组件(对象后置处理器、AuthenticationManagerBuilder、共享对象)
HttpSecurity http = new HttpSecurity(this.objectPostProcessor, authenticationBuilder, this.createSharedObjects());
// 4. 配置 WebAsyncManager 集成过滤器(异步请求安全上下文传递)
WebAsyncManagerIntegrationFilter webAsyncManagerIntegrationFilter = new WebAsyncManagerIntegrationFilter();
webAsyncManagerIntegrationFilter.setSecurityContextHolderStrategy(this.securityContextHolderStrategy);
// 5. 应用默认配置:CSRF、异常处理、头部安全、会话管理、登出等
http
// CSRF 防护
.csrf(Customizer.withDefaults())
// 异步请求集成
.addFilter(webAsyncManagerIntegrationFilter)
// 异常处理(默认配置)
.exceptionHandling(Customizer.withDefaults())
// 头部安全(默认配置)
.headers(Customizer.withDefaults())
// 会话管理(默认配置)
.sessionManagement(Customizer.withDefaults())
// 登出(默认配置)
.logout(Customizer.withDefaults());
// 6. 应用 CORS 配置(如果存在 CorsFilter Bean)
this.applyCorsIfAvailable(http);
// 7. 应用所有默认的 Configurer(如表单登录、HTTP Basic 等)
this.applyDefaultConfigurers(http);
return http;
}

首先初始化了AuthenticationManagerBuilderhttpSecurity是重新new了一个authenticationManagerBuilder的,并且我们初始化的authenticationManager是放在 builder 对象里的parentAuthenticationManager属性里的。

然后我们看到httpSecurity的构造方法

image-20250925123440533

可以发现是往一个所谓的共享对象的 Map 容器中放入了authenticationManagerBuilder对象

1
this.setSharedObject(AuthenticationManagerBuilder.class, authenticationBuilder);

初始化的时候还放入了spring上下文对象

image-20250925123712042

可以发现httpSecuritysharedObjects属性里放入了所谓需要共享的对象,另外注意这里requestMatcherConfigurer这个属性也进行了初始化。

接下来就是使用httpSecurity进行了默认配置,在 HttpSecurity 中,通过 getOrApply 方法来获取或应用配置器(如 CsrfConfigurer)。往上看你就会发现几乎所有的配置器都调用的了这个方法进行装配

image-20250925124843207
  • 首先尝试从已有的配置器中获取指定类型(这里是 CsrfConfigurer 类型)的配置器。
  • 如果存在,直接返回;如果不存在,就调用 apply 方法来应用这个新的配置器。

apply 方法会进一步调用 add 方法,将配置器添加到 configurers 集合中(configurersAbstractConfiguredSecurityBuilder 中的一个 LinkedHashMap,用于存储各种配置器),这样就完成了配置器的注册,后续可以基于这些配置器来构建安全过滤链等安全相关功能。这里实际上就是将上面 new 的 configure 放入了 configurers属性中了。

接下来我们来看过滤器是如何装配的

addFilter方法顾名思义就是添加过滤器的方法

image-20250925125035377
  • 首先从 filterOrders 中获取要添加的过滤器(如 WebAsyncManagerIntegrationFilter)对应的顺序。filterOrdersFilterOrderRegistration 类型的对象,内部维护了过滤器类与顺序的映射关系。
  • 如果获取不到顺序,会抛出异常,提示需要使用 addFilterBeforeaddFilterAfter 来指定过滤器顺序;如果获取到顺序,就将过滤器包装成 OrderedFilter(包含过滤器实例和顺序)并添加到 filters 列表中。

最后装完的filters 是一个 ArrayList,用于存储所有添加的过滤器(包装为 OrderedFilter 形式,带有顺序信息)。在构建安全过滤链时,这些过滤器会按照顺序被组织起来,对 HTTP 请求进行安全处理,比如身份验证、授权、跨站请求伪造(CSRF)防护等。

1
private List<OrderedFilter> filters = new ArrayList();

而决定其过滤器顺序的就是filterOrders, 是 FilterOrderRegistration 类型,其内部通过 filterToOrder 等结构维护了不同过滤器类对应的顺序。在 FilterOrderRegistration 的构造方法中,会为各种 Spring Security 内置的过滤器(如 CsrfFilterLogoutFilterUsernamePasswordAuthenticationFilter 等)预先注册好顺序,这样在添加过滤器时,就能根据过滤器类获取到对应的顺序,从而保证过滤器在过滤链中有正确的执行顺序。

这样,不同的过滤器就会按照预先定义的顺序在请求处理流程中依次执行,确保 Spring Security 各项安全功能按正确逻辑生效。

总结一下就是,httpSecurity默认初始化会出事后一个authenticationManager对象并放入一个authenticationManagerBuilder对象中,随后这个 builder 对象会放入 httpSecurity 对象的 sharedObjects属性中; 然后 httpSecurity 会创建类如csrf,exceptionHandling等等的配置器,随后也放入 sharedObjects 属性中。额案后将配置器添加到 configurers 集合中,之后再搭建过滤器链,从 filterOrders 中获取要添加的过滤器,在构建安全过滤链时,这些过滤器会按照顺序被组织起来,实现其功能,这样就联系上了前面进行的 16 个默认过滤器的装配和各种配置的初始化。

HttpSecurity如何构建过滤器链

HttpSecurity如何构建DefaultSecurityFilterChain过滤器链

那么其实HttpSecurity 的使命是构建 DefaultSecurityFilterChain(安全过滤链),流程分为两步

配置阶段:通过配置器定义规则

开发者通过 HttpSecurity 提供的配置器方法(如 formLogin()authorizeHttpRequests()),声明各类安全规则:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
// 配置器1:表单登录
.formLogin(form -> form
.loginPage("/login")
.defaultSuccessUrl("/home")
)
// 配置器2:请求授权
.authorizeHttpRequests(auth -> auth
.requestMatchers("/public/**").permitAll()
.requestMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
// 配置器3:CSRF 防护
.csrf(csrf -> csrf.disable());
return http.build();
}

每个配置器(如 FormLoginConfigurerAuthorizeHttpRequestsConfigurer)会将配置转换为具体的 Filter(过滤器),并注册到 HttpSecurity 中。

构建阶段:生成安全过滤链

image-20250925130218452

当调用 http.build() 时,HttpSecurity 会执行:

  1. 收集所有配置器生成的 Filter;
  2. 按照预设顺序(由 FilterOrderRegistration 管理)排序 Filter;
  3. 结合请求匹配规则(RequestMatcher),封装为 DefaultSecurityFilterChain
  4. DefaultSecurityFilterChain 注册到 Spring Security 过滤器链中,对 HTTP 请求进行拦截校验。

我们展开说一下,HttpSecurity 继承自 AbstractConfiguredSecurityBuilder,其 build() 方法会触发配置器的初始化和过滤器的收集。

收集所有配置器生成的 Filter

核心逻辑在 AbstractConfiguredSecurityBuilderdoBuild() 方法中,该方法会依次调用所有配置器(如 CsrfConfigurerFormLoginConfigurer 等)的 init()configure() 方法,配置器在这些方法中生成对应的过滤器并添加到 HttpSecurityfilters 列表中。

image-20250925125759860
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Override
protected final O doBuild() throws Exception {
synchronized (this.configurers) {
this.buildState = BuildState.INITIALIZING;
// 初始化所有配置器(调用 configurer.init())
init();
// 配置所有配置器(调用 configurer.configure())
this.buildState = BuildState.CONFIGURING;
configure();
// 执行实际构建(调用子类的 performBuild())
this.buildState = BuildState.BUILDING;
O result = performBuild();
this.buildState = BuildState.BUILT;
return result;
}
}

配置器生成过滤器的示例(以 CsrfConfigurer 为例),CsrfConfigurerconfigure() 方法中创建 CsrfFilter 并添加到 HttpSecurity 的过滤器列表:

1
2
3
4
5
@Override
public void configure(H http) {
CsrfFilter filter = createCsrfFilter(http);
http.addFilter(filter); // 添加到 HttpSecurity 的 filters 列表
}

按照预设顺序排序 Filter

然后来到了第二步,过滤器的排序由 FilterOrderRegistration 管理,最终在 HttpSecurityperformBuild() 方法中完成排序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Override
protected DefaultSecurityFilterChain performBuild() {
// 对过滤器按顺序排序(OrderComparator 基于 Ordered 接口的 getOrder())
this.filters.sort(OrderComparator.INSTANCE);

// 提取排序后的过滤器(去除 Ordered 包装)
List<Filter> sortedFilters = new ArrayList<>(this.filters.size());
for (Filter filter : this.filters) {
sortedFilters.add(((OrderedFilter) filter).filter);
}

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

FilterOrderRegistration 预先定义了所有内置过滤器的顺序

封装为 DefaultSecurityFilterChain

排序完成后,HttpSecurity 会将过滤器列表与请求匹配规则(RequestMatcher)结合,创建 DefaultSecurityFilterChain 实例。核心就是最后一步,前面都是排序

1
2
3
4
5
6
7
@Override
protected DefaultSecurityFilterChain performBuild() {
// ... 前面的排序逻辑省略 ...

// 结合请求匹配器和排序后的过滤器,创建 DefaultSecurityFilterChain
return new DefaultSecurityFilterChain(this.requestMatcher, sortedFilters);
}
image-20250925130009639

DefaultSecurityFilterChain 的构造方法如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class DefaultSecurityFilterChain implements SecurityFilterChain {
private final RequestMatcher requestMatcher;
private final List<Filter> filters;

public DefaultSecurityFilterChain(RequestMatcher requestMatcher, List<Filter> filters) {
this.requestMatcher = requestMatcher;
this.filters = new ArrayList<>(filters);
}

// 判断当前请求是否匹配该过滤链
@Override
public boolean matches(HttpServletRequest request) {
return this.requestMatcher.matches(request);
}

// 获取过滤链中的过滤器
@Override
public List<Filter> getFilters() {
return this.filters;
}
}

注册到 Spring Security 过滤器链

DefaultSecurityFilterChain 生成后,会被注册到 FilterChainProxy 中,而 FilterChainProxy 作为一个顶级过滤器(Filter)被 Spring 容器管理,最终拦截所有 HTTP 请求并应用对应的安全过滤链。

FilterChainProxy 会根据请求匹配规则(RequestMatcher)选择对应的 DefaultSecurityFilterChain 处理请求:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class FilterChainProxy extends GenericFilterBean {
private final List<SecurityFilterChain> filterChains;

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
doFilterInternal(request, response, chain);
}

private void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;

// 根据请求匹配规则选择对应的 SecurityFilterChain
List<Filter> filters = getFilters(httpRequest);
if (filters == null || filters.isEmpty()) {
chain.doFilter(request, response);
return;
}

// 执行过滤链中的所有过滤器
VirtualFilterChain vfc = new VirtualFilterChain(chain, filters);
vfc.doFilter(httpRequest, httpResponse);
}

// 根据请求匹配器获取对应的过滤器列表
private List<Filter> getFilters(HttpServletRequest request) {
for (SecurityFilterChain chain : this.filterChains) {
if (chain.matches(request)) {
return chain.getFilters();
}
}
return null;
}
}

HttpSecurity如何初始化SecurityFilterChain过滤器链

先回忆一下 Spring Security 的过滤器叫FilterChainProxy,其中存储过滤器的属性叫filterChains,集合里元素的类型时SecurityFilterChain

image-20250925130455608

因此,我们前面做了那么多,肯定是为了这个SecurityFilterChain来做准备的,下面我们看 SpringBootWebSecurityConfiguration 这个类

image-20250925130537156

进入到这个方法也是很熟悉啊,我们之前说的csrf()formLogin() 等配置方法 实现模式完全一致,也是做了通过 配置器(Configurer) 收集用户的安全配置的工作(如授权规则、CSRF 开关、登录页设置等),并在后续的 build() 阶段由配置器生成对应的过滤器,最终整合到安全过滤链中。

也是执行了getOrApply来管理配置器

1
2
3
4
5
6
7
public HttpSecurity authorizeHttpRequests(Customizer<AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry> authorizeHttpRequestsCustomizer) throws Exception {
// 1. 获取 Spring 上下文
ApplicationContext context = this.getContext();
// 2. 通过 getOrApply 获取/创建配置器,并获取其内部配置接口(AuthorizationManagerRequestMatcherRegistry)
authorizeHttpRequestsCustomizer.customize(((AuthorizeHttpRequestsConfigurer)this.getOrApply(new AuthorizeHttpRequestsConfigurer(context))).getRegistry());
return this;
}

管中窥豹,我们可以得出,Spring Security 通过这种 “配置器模式 + 链式调用” 的设计,将分散的安全配置(授权、认证、防护等)统一管理,既保证了扩展性(可自定义配置器),又简化了使用方式(链式 API)。

image-20250925131342096

从这个类结构图中我们不难发现,HttpSecurityAuthenticationManagerBuilder都是这个 builder 类的子类,所以说实现的逻辑都是一样的,但是,我们 HttpSecurity 的构建就要复杂一点了

前面我们知道了,httpSecurity初始化的时候创建了很多 configure 并且最终是放入到他的 map 集合中的,之前我们提到了 doBuild()方法实现了配置器生成对应的过滤器然后装配的,那么核心的流程就涉及到init()configure() 方法了,他们定义在 AbstractConfiguredSecurityBuilder 类中,是构建安全组件的核心方法。

image-20250925132028518

init() 方法对所有已注册的 SecurityConfigurer(如 CsrfConfigurerAuthorizeHttpRequestsConfigurer 等)进行初始化操作。在初始化阶段,配置器可以做一些前置准备工作,比如设置一些基础的配置参数、初始化内部依赖等,但还不会实际生成过滤器等核心安全组件。可以看到它在构建过程的早期阶段,为后续的配置阶段做准备。而 beforeinit()则涉及到了更早期的操作,类似 AOP 的前置消息

1
2
3
4
5
6
7
8
9
private void init() throws Exception {
Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
for (SecurityConfigurer<O, B> configurer : configurers) {
configurer.init((B) this);
}
for (SecurityConfigurer<O, B> configurer : this.configurersAddedInInitializing) {
configurer.init((B) this);
}
}

configure() 方法调用每个 SecurityConfigurerconfigure 方法,让配置器根据用户的配置(比如授权规则、CSRF 防护开关等)来生成对应的安全组件,像各种过滤器(CsrfFilterFilterSecurityInterceptor 等)就是在这个阶段由配置器创建并添加到 HttpSecurityfilters 列表中的,可以看到他执行在 init() 方法执行之后,是配置器实际发挥作用、生成安全组件的关键阶段。

1
2
3
4
5
6
private void configure() throws Exception {
Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
for (SecurityConfigurer<O, B> configurer : configurers) {
configurer.configure((B) this);
}
}

init方法和configure方法都是遍历的configurers然后执行每一个configure的配置方法的,configurers 是一个 存储所有安全配置器的容器,定义如下:

1
2
// AbstractConfiguredSecurityBuilder 中定义
private final LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>> configurers;
  • 这是一个 LinkedHashMap键是配置器的类型(Class值是该类型的配置器列表(通常一个类型只有一个配置器)。
  • 目的是按类型管理所有注册到 HttpSecurity 中的配置器(如 CsrfConfigurerAuthorizeHttpRequestsConfigurer 等),方便后续遍历和操作。

init()configure() 方法中遍历的 configurers,正是通过 getConfigurers() 方法提取的:

1
2
3
4
5
6
7
8
9
// AbstractConfiguredSecurityBuilder 中定义
private Collection<SecurityConfigurer<O, B>> getConfigurers() {
List<SecurityConfigurer<O, B>> result = new ArrayList();
// 遍历 configurers 中所有类型的配置器列表,汇总为一个集合
for(List<SecurityConfigurer<O, B>> configs : this.configurers.values()) {
result.addAll(configs);
}
return result;
}
  • 作用:将 configurers 中按类型分组的配置器,汇总为一个扁平的 Collection,供 init()configure() 遍历。
  • 例如:如果注册了 CsrfConfigurerFormLoginConfigurerAuthorizeHttpRequestsConfigurergetConfigurers() 会返回包含这三个配置器的集合。

所有配置器(如 CsrfConfigurer)都实现了 SecurityConfigurer 接口(通常继承 SecurityConfigurerAdapter),因此都有 init()configure() 方法。我们挑CsrfConfigurer 方法来实际看一下

HttpSecurity 中,通过 http.csrf() 方法触发 CsrfConfigurer 的创建与注册

1
2
3
4
public CsrfConfigurer<HttpSecurity> csrf() throws Exception {
ApplicationContext context = this.getContext();
return (CsrfConfigurer)this.getOrApply(new CsrfConfigurer(context));
}
  • getOrApply(new CsrfConfigurer(context)):若已存在 CsrfConfigurer,直接返回;否则新建并注册configurers 容器中。

CsrfConfigurer 虽未显式重写 init(),但会继承父类 AbstractHttpConfigurerinit()(空实现,可按需重写)。若有初始化逻辑,会在此阶段执行,新版本中,init()的流程貌似都交给了大家来处理

CsrfConfigurerconfigure() 是核心,负责创建 CsrfFilter 并注册到 HttpSecurity 的过滤器链:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Override
public void configure(H http) {
// 1. 创建 CsrfFilter,传入 CSRF 令牌仓库
CsrfFilter filter = new CsrfFilter(this.csrfTokenRepository);
// 2. 配置 CSRF 防护的请求匹配规则
RequestMatcher requireCsrfProtectionMatcher = getRequireCsrfProtectionMatcher();
if (requireCsrfProtectionMatcher != null) {
filter.setRequireCsrfProtectionMatcher(requireCsrfProtectionMatcher);
}
// 3. 配置访问拒绝处理器(处理 CSRF 校验失败)
AccessDeniedHandler accessDeniedHandler = createAccessDeniedHandler(http);
filter.setAccessDeniedHandler(accessDeniedHandler);
// 4. 与其他配置器联动(如 LogoutConfigurer、SessionManagementConfigurer)
LogoutConfigurer<H> logoutConfigurer = http.getConfigurer(LogoutConfigurer.class);
if (logoutConfigurer != null) {
logoutConfigurer.addLogoutHandler(new CsrfLogoutHandler(this.csrfTokenRepository));
}
// 5. 注册过滤器到 HttpSecurity 的过滤器列表
http.addFilter(filter);
}

当调用 http.build() 时,进入 performBuild() 阶段,最终参与 SecurityFilterChain 构建:

1
2
3
4
5
6
7
8
9
10
11
protected DefaultSecurityFilterChain performBuild() {
........
// 1. 对所有过滤器(含 CsrfFilter、UsernamePasswordAuthenticationFilter 等)排序
this.filters.sort(OrderComparator.INSTANCE);
List<Filter> sortedFilters = new ArrayList<>(this.filters.size());
for (Filter filter : this.filters) {
sortedFilters.add(filter);
}
// 2. 结合请求匹配器,创建 DefaultSecurityFilterChain
return new DefaultSecurityFilterChain(this.requestMatcher, sortedFilters);
}

这里我们看到,它先把 filter里面的过滤器排序,遍历然后放入到sortedFilters,最后创建了一个DefaultSecurityFilterChain对象,至此,一个SecurityFilterChain创建完毕,这里构造方法里还有个requestMatcher属性留意一下,其实就是 Spring Security 的FilterChainProxy中的filterChains是一个集合,所以支持多个,那当有多个SecurityFilterChain时,一个请求进来会使用哪个SecurityFilterChain就是由这个requestMatcher决定的

此时,一个完整的 SecurityFilterChain 诞生了:它包含 排序后的过滤器列表(如 CsrfFilterUsernamePasswordAuthenticationFilter 等)和 请求匹配规则requestMatcher)。

在 Spring Security 启动时,所有 HttpSecurity 构建的 DefaultSecurityFilterChain 会被收集到 FilterChainProxyfilterChains 集合中

当用户发送请求(如 POST /web/user)时,流程进入运行阶段,核心是通过 requestMatcher 选择合适的过滤链:

所有请求会先经过 FilterChainProxydoFilter 方法,这是 Spring Security 的 “守门人

FilterChainProxydoFilter 方法接收请求(Spring Security 过滤链的 “总入口”),是请求进入 Spring Security 过滤链的第一步,核心逻辑如下,它作为所有安全过滤逻辑的入口,通过 doFilterInternal 方法进入真正的过滤链匹配与执行流程

image-20250925134702027

doFilterInternal 方法中,会调用 getFilters 方法筛选出与当前请求匹配的 SecurityFilterChaingetFilters 方法遍历 filterChains 集合,通过每个链的 requestMatcher 判断是否匹配当前请求:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// 1. 对请求和响应做防火墙包装(安全增强)
FirewalledRequest firewallRequest = this.firewall.getFirewalledRequest((HttpServletRequest) request);
HttpServletResponse firewallResponse = this.firewall.getFirewalledResponse((HttpServletResponse) response);
// 2. 关键:调用 getFilters 筛选匹配的 SecurityFilterChain,并获取其过滤器列表
List<Filter> filters = this.getFilters((HttpServletRequest) firewallRequest);
if (filters != null && filters.size() != 0) {
// 有匹配的过滤器链,执行过滤逻辑(装饰器模式包装后执行)
FilterChain reset = (ServletRequest req, ServletResponse res) -> {
firewallRequest.reset();
chain.doFilter(req, res);
};
this.filterChainDecorator.decorate(reset, filters).doFilter(firewallRequest, firewallResponse);
} else {
// 无匹配的过滤器链,直接放行
firewallRequest.reset();
chain.doFilter(firewallRequest, firewallResponse);
}
}
image-20250925134728027

getFilters 方法会遍历所有 SecurityFilterChain,通过 requestMatcher 判断是否匹配当前请求,一旦找到匹配的 SecurityFilterChain,其包含的过滤器会按顺序执行

其中chain.matches(request) —— 每个 SecurityFilterChain 都有自己的 requestMatcher(请求匹配器),只有当请求与 requestMatcher 匹配时,才会使用该链的过滤器。

image-20250925134752889

那么FilterChainsProxy是从哪里来的呢,FilterChainProxy 是 Spring Security 过滤链的顶层代理过滤器,它的创建和注册与 WebSecurity 密切相关

image-20250925135647988

WebSecurity 是 Spring Security 中管理全局安全过滤链的核心构建器(继承自 AbstractConfiguredSecurityBuilder),它的 performBuild() 方法最终会创建 FilterChainProxy 实例,代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
protected Filter performBuild() throws Exception {
// 1. 收集所有 SecurityFilterChain(包括忽略的请求链和业务过滤链)
List<SecurityFilterChain> securityFilterChains = new ArrayList();
// ① 添加“忽略请求”对应的空过滤链(如配置的 ignoredRequests)
for(RequestMatcher ignoredRequest : this.ignoredRequests) {
SecurityFilterChain securityFilterChain = new DefaultSecurityFilterChain(ignoredRequest, new Filter[0]);
securityFilterChains.add(securityFilterChain);
}
// ② 添加通过 HttpSecurity 构建的业务过滤链
for(SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : this.securityFilterChainBuilders) {
SecurityFilterChain securityFilterChain = securityFilterChainBuilder.build();
securityFilterChains.add(securityFilterChain);
}

// 2. 创建 FilterChainProxy,传入所有收集到的 SecurityFilterChain
FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);

// 3. 配置防火墙、请求拒绝处理器等附加属性
if (this.httpFirewall != null) {
filterChainProxy.setFirewall(this.httpFirewall);
}
if (this.requestRejectedHandler != null) {
filterChainProxy.setRequestRejectedHandler(this.requestRejectedHandler);
}

// 4. 如果开启调试模式,用 DebugFilter 包装
Filter result = filterChainProxy;
if (this.debugEnabled) {
result = new DebugFilter(filterChainProxy);
}

return result; // 最终返回的就是 FilterChainProxy(或其包装类)
}

也就是说,FilterChainProxyWebSecurityperformBuild() 方法中主动创建的,它的构造参数是所有收集到的 SecurityFilterChain,而WebSecurity 本身的初始化和 FilterChainProxy 的创建,依赖于 Spring Security 的自动配置机制,核心流程如下:

  • 当我们在项目中添加 @EnableWebSecurity 注解时,会导入 WebSecurityConfiguration 配置类,这个类是 WebSecurity 的 “孵化器”。

  • WebSecurityConfiguration 中,会通过 @Bean 方法创建 WebSecurity 对象,并为其配置必要的依赖(如 ObjectPostProcessorApplicationContext 等):

    image-20250925135925412
  • 然后,其中我们通过 SecurityConfiguration 中定义的 SecurityFilterChain Bean,会被 WebSecurityConfiguration 自动收集,这个方法也就是接收并存储外部配置好的 SecurityFilterChain 列表

    image-20250925140031540
  • 然后通过这个方法过滤器被添加,通过 webSecurity.addSecurityFilterChainBuilder(...) 方法添加到 WebSecuritysecurityFilterChainBuilders 列表中(对应 WebSecurity 源码中的 securityFilterChainBuilders 集合)。

    image-20250925140227493
  • 最后,当 Spring 容器初始化时,会调用 WebSecuritybuild() 方法(继承自 AbstractSecurityBuilder),最终执行 performBuild() 方法,创建 FilterChainProxy 并作为 @Bean 注册到 Spring 容器中。而 webSecurity 和httpSecurity 也是同一个父类,因此 webSecurity 的 build 方法也是同样的逻辑

  • 至此,FilterChainsProxy也创建完毕了。

最后,FilterChainProxy 作为 Spring 管理的 Filter 类型 Bean,会被 Spring 的 DelegatingFilterProxy 代理,并注册到 Servlet 容器(如 Tomcat)的过滤器链中。

流程如下:

  1. Spring 容器启动时,DelegatingFilterProxy 作为 Servlet 过滤器被注册(通过 AbstractSecurityWebApplicationInitializer 自动配置)。

  2. DelegatingFilterProxy 会从 Spring 容器中查找名为 springSecurityFilterChainFilter Bean(默认就是 WebSecurity 构建的 FilterChainProxy)。

  3. 当请求到达 Servlet 容器时,会先经过 DelegatingFilterProxy,再转发给 FilterChainProxy 执行实际的安全过滤逻辑。

然后我们就进行梳理HttpSecurity 初始化 SecurityFilterChain 过滤器链的流程

当调用 http.build()HttpSecurity 继承自 SecurityBuilderbuild 方法用于构建最终的安全组件)时,会触发以下关键步骤来初始化 SecurityFilterChain

  • 执行 init() 方法,遍历所有注册到 HttpSecuritySecurityConfigurer,调用它们的 init 方法,完成配置器的初始化准备。

  • 执行 configure() 方法,再次遍历所有 SecurityConfigurer,调用它们的 configure 方法。在这个过程中,各个配置器会根据自身的配置逻辑,生成对应的过滤器,并将这些过滤器添加到 HttpSecurityfilters 列表中。例如

    • CsrfConfigurer 会创建 CsrfFilter 并添加到 filters
    • AuthorizeHttpRequestsConfigurer 会创建 FilterSecurityInterceptor 来处理授权规则,并添加到 filters
  • 构建 DefaultSecurityFilterChain,在 HttpSecurityperformBuild 方法中,会对 filters 列表中的过滤器按照预设的顺序(由 FilterOrderRegistration 管理)进行排序,然后结合请求匹配器(RequestMatcher,用于判断哪些请求需要经过该过滤链),创建 DefaultSecurityFilterChain 实例

  • 注册 SecurityFilterChain,生成的 DefaultSecurityFilterChain 会被注册到 FilterChainProxy 中。FilterChainProxy 是 Spring Security 过滤器链的代理,它会根据请求的匹配情况,选择对应的 SecurityFilterChain 来处理请求,从而实现对 HTTP 请求的安全拦截与校验。

所以说,HttpSecurity 初始化 SecurityFilterChain 过滤器链的过程,是通过 init() 方法初始化配置器,configure()方法让配置器生成过滤器,再对过滤器排序并结合请求匹配器构建 DefaultSecurityFilterChain,最后将其注册到 FilterChainProxy 中,以此来实现对 HTTP 请求的安全过滤。