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 防护),通过 HttpSecurity 的 xxxConfigurer() 方法接入

然后,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;
}

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

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

image-20250925123440533

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

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

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

image-20250925123712042

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

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

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

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

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

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

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

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

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

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

这样,不同的过滤器就会按照预先定义的顺序在请求处理流程中依次执行,确保 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();
}

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

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

image-20250925130218452

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

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

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

收集所有配置器生成的 Filter

核心逻辑在 AbstractConfiguredSecurityBuilder 的 doBuild() 方法中,该方法会依次调用所有配置器(如 CsrfConfigurer、FormLoginConfigurer 等)的 init() 和 configure() 方法,配置器在这些方法中生成对应的过滤器并添加到 HttpSecurity 的 filters 列表中。

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 为例),CsrfConfigurer 在 configure() 方法中创建 CsrfFilter 并添加到 HttpSecurity 的过滤器列表:

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

按照预设顺序排序 Filter

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

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

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

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

image-20250925132028518

init() 方法对所有已注册的 SecurityConfigurer(如 CsrfConfigurer、AuthorizeHttpRequestsConfigurer 等)进行初始化操作。在初始化阶段,配置器可以做一些前置准备工作,比如设置一些基础的配置参数、初始化内部依赖等,但还不会实际生成过滤器等核心安全组件。可以看到它在构建过程的早期阶段,为后续的配置阶段做准备。而 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() 方法调用每个 SecurityConfigurer 的 configure 方法,让配置器根据用户的配置(比如授权规则、CSRF 防护开关等)来生成对应的安全组件,像各种过滤器(CsrfFilter、FilterSecurityInterceptor 等)就是在这个阶段由配置器创建并添加到 HttpSecurity 的 filters 列表中的,可以看到他执行在 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 中的配置器(如 CsrfConfigurer、AuthorizeHttpRequestsConfigurer 等),方便后续遍历和操作。

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() 遍历。
  • 例如:如果注册了 CsrfConfigurer、FormLoginConfigurer、AuthorizeHttpRequestsConfigurer,getConfigurers() 会返回包含这三个配置器的集合。

所有配置器(如 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(),但会继承父类 AbstractHttpConfigurer 的 init()(空实现,可按需重写)。若有初始化逻辑,会在此阶段执行,新版本中,init()的流程貌似都交给了大家来处理

CsrfConfigurer 的 configure() 是核心,负责创建 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 诞生了:它包含 排序后的过滤器列表(如 CsrfFilter、UsernamePasswordAuthenticationFilter 等)和 请求匹配规则(requestMatcher)。

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

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

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

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

image-20250925134702027

在 doFilterInternal 方法中,会调用 getFilters 方法筛选出与当前请求匹配的 SecurityFilterChain, getFilters 方法遍历 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(或其包装类)
}

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

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

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

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

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

    image-20250925140227493
  • 最后,当 Spring 容器初始化时,会调用 WebSecurity 的 build() 方法(继承自 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 容器中查找名为 springSecurityFilterChain 的 Filter Bean(默认就是 WebSecurity 构建的 FilterChainProxy)。

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

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

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

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

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

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

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

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