恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

Spring Security 适配器源码:过滤器链与安全配置原理

  • 首页
  • 资讯中心
  • /
  • Spring Security 适配器源码:过滤器链与安全配置原理

相关资讯

Linux usleep 延时不准的根因排查与高精度替代方案 2026/10/15 20:11:42
通信算法岗笔试核心能力拆解:信道建模到链路预算的工程闭环 2026/10/15 20:06:42
基于STM32的自制游戏手柄:摇杆ADC采样、按键扫描与HID输出实战 2026/10/15 20:06:42

最新资讯

VSCode 中配置 DeepSeek API 密钥指南:把 settings.json 改到 TaoToken
项目成本管理实战指南:从估算到挣值分析的全流程解析
CPU性能工程实测:SoA比AoS快14倍——SIMD数据布局的完整规则
jdk1.7.0_13 现代生存指南:安装、构建与避坑
算法导论英文原版第三版PDF:从伪代码到红黑树实现与复杂度分析
Visual Studio Hub 介绍:用 TaoToken 统一管理多 AI 编码助手的配置入口

今日推荐

AI视频生成的时间一致性难题与时空建模演进
基于bbox的PDF文档解析:多栏重排与水印过滤实战
探矿数据进RAG前的清洗全攻略:TXT、Word、PDF、网页四类文档处理实践

本周热门

UE动画修改实战:从资产编辑到重定向与蒙太奇驱动
统计随机数生成器攻击下的KLJN安全密钥交换协议Matlab仿真
政务API安全治理:资产测绘、低代码编排与行标对标实践

本月精选

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)

Spring Security 适配器源码:过滤器链与安全配置原理

发布时间:2026/10/15 20:11:42
Spring Security 适配器源码:过滤器链与安全配置原理 1. 一个适配器凭什么撑起整个安全配置体系1.1 从过滤器链到HttpSecurity安全配置的演进逻辑早几年我接手一个老项目安全配置类全部继承WebSecurityConfigurerAdapter上面堆了几百行http.authorizeRequests()、auth.userDetailsService()。当时觉得这就是Spring Security的标配写法直到有一次排查一个“同一个接口在生产环境不需要认证、测试环境却被拦截”的问题光是改配置就来回折腾了两天。后来我干脆把启动链路的源码翻了一遍才真正明白这个类在整个安全框架里的位置。先抛开Spring Security回到Servlet规范本身。Servlet容器只认识Servlet和Filter它不知道什么叫“登录”“角色”“权限”。Spring Security做的事情就是把认证、授权、会话管理这些能力编码成一条又一条过滤器装进一个过滤器链里然后通过DelegatingFilterProxy注入到Servlet容器。你写安全配置本质就是在描述“这条链上应该有哪些过滤器、按什么顺序执行、对哪些请求生效”。那问题来了这条过滤器链的长度相当可观。光是最常见的配置就会涉及登录、登出、Session、CSRF、异常处理、SecurityContext、RememberMe等十几个过滤器。如果让你写代码时手动把这些过滤器一个个new出来再排序代码会膨胀到没法维护。Spring Security的Java Config体系给出的答案是引入构建器把零散的配置动作翻译成过滤器的注册逻辑。WebSecurityConfigurerAdapter就是这个翻译过程的桥头堡。1.2 Adapter到底“适配”了什么看类声明public abstract class WebSecurityConfigurerAdapter implements WebSecurityConfigurerWebSecurity而WebSecurityConfigurerWebSecurity继承自SecurityConfigurerFilter, WebSecurity。这里的泛型含义很直接Filter是最终要生产出来的产品WebSecurity是用来生产这个产品的构建器。SecurityConfigurer接口里有两个关键方法init(Builder builder)和configure(Builder builder)。它们的调用时机不同init在构建器进入初始化阶段时执行configure在初始化完成后、真正构建之前执行。这两个方法就是框架留给所有扩展点的“改造入口”。WebSecurityConfigurerAdapter做的事情是把这两个底层钩子封装成更符合开发者直觉的形态。你在子类里见到的是三个protected方法configure(AuthenticationManagerBuilder auth)、configure(HttpSecurity http)、configure(WebSecurity web)。你不需要关心init和configure何时被调用只需要在正确的入口写你要的规则。适配器模式的名字不是白叫的——它把复杂构建器的内在流程适配成了三句语义清晰的业务配置入口。2. 从EnableWebSecurity开始追踪配置类的装配链路2.1 EnableWebSecurity到底Import了什么绝大多数Spring Boot项目里安全配置的起点是EnableWebSecurity注解。源码也很简单Configuration Import(WebSecurityConfiguration.class) public interface EnableWebSecurity {}关键在Import(WebSecurityConfiguration.class)这一行。WebSecurityConfiguration才是真正干活的类它是Spring容器中安全配置的总调度点。这个类里有一堆Autowired注入点其中最值得关注的是这个Autowired(required false) private ListSecurityConfigurerFilter, WebSecurity webSecurityConfigurers;Spring在容器初始化时会把所有满足SecurityConfigurerFilter, WebSecurity类型约束的Bean收集到这个集合里。因为WebSecurityConfigurerAdapter实现了这个接口所以容器中所有继承它的配置类都会自动出现在这个集合中。这就是为什么你只要写一个继承WebSecurityConfigurerAdapter的配置类并标注Configuration它就会被框架发现并生效——不需要任何手动注册。2.2 一条Filter Bean的诞生过程WebSecurityConfiguration里定义了一个名字固定的BeanBean(name springSecurityFilterChain) public Filter springSecurityFilterChain() throws Exception { ... for (SecurityConfigurerFilter, WebSecurity webSecurityConfigurer : webSecurityConfigurers) { webSecurity.apply(webSecurityConfigurer); } return webSecurity.build(); }这个Bean的名字很关键。Servlet容器端的DelegatingFilterProxy默认会到Spring容器里找名为springSecurityFilterChain的过滤器找到的实际上是FilterChainProxy的实例。也就是说你看到的安全入口就是在这一行webSecurity.build()里被最终构建出来的。在build()调用背后是AbstractConfiguredSecurityBuilder的标准构建流程。它分四个阶段init()、configure()、performBuild()。每个阶段都会遍历当前构建器上挂载的所有SecurityConfigurer依次调用对应的生命周期方法。WebSecurityConfigurerAdapter作为其中一个SecurityConfigurer也会在合适的时机收到回调。2.3 init与configure的两次回调WebSecurityConfigurerAdapter自己实现了init(WebSecurity web)方法主要做一些前置工作比如设置调试模式、绑定安全拦截器等。子类很少重写这个方法大多数时候我们接触的是configure(WebSecurity web)。configure(WebSecurity web)在WebSecurity构建流程的configure阶段被调用。你在这个方法里写的web.ignoring().antMatchers(/static/**)会被解析成“这些路径完全不经过安全过滤器链”的规则。第一次看源码的人容易把configure(WebSecurity web)和configure(HttpSecurity http)搞混觉得都是配放行路径。实际上它们面对的是不同层级的构建器一个作用于顶层WebSecurity一个作用于单条过滤器链HttpSecurity。后面我会专门讲这个区别但你先记住这个大方向WebSecurity管理的是整条安全过滤器链的组合和顶层策略HttpSecurity管理的是某一条具体链内部过滤器的装配。3. 三个configure方法分别被谁调用、何时调用、为何调用3.1 configure(AuthenticationManagerBuilder)认证管理器的局部与全局第一个configure(AuthenticationManagerBuilder auth)负责描述“认证管理器”怎么创建。它被调用的位置在WebSecurityConfigurerAdapter.getHttp()内部准确地说是在获取AuthenticationManager的时候。getHttp()里有一个authenticationManager()私有方法逻辑大致是这样protected AuthenticationManager authenticationManager() throws Exception { if (!this.authenticationManagerInitialized) { configure(this.localConfigureAuthenticationBldr); if (this.localConfigureAuthenticationBldr.isConfigured()) { this.authenticationManager this.localConfigureAuthenticationBldr.build(); } else { this.authenticationManager getAuthenticationConfiguration().getAuthenticationManager(); } this.authenticationManagerInitialized true; } return this.authenticationManager; }这逻辑挺有意思。先尝试调用你重写的configure(auth)如果你在这个方法里注册了UserDetailsService、PasswordEncoder或者自定义AuthenticationProvider那么localConfigureAuthenticationBldr.isConfigured()会返回true并基于你提供的这些组件构建出一个“局部AuthenticationManager”。如果子类没有重写这个方法或者重写后什么都没配置框架就回退到全局的AuthenticationConfiguration从容器中获取已经定义好的认证管理器。这可以解释一个常见困惑为什么有时候AuthenticationManager像“有两个”一个是你自己配的一个是容器里的。源码给出的答案很明确——优先使用局部构建器只有当局部构建器没有被“配置过”时才降级到全局。3.2 configure(HttpSecurity)决定过滤器链内容的核心入口第二个configure(HttpSecurity http)是大多数人最常重写的方法。它也是由getHttp()方法在合适的时机调用的。当你调用http.authorizeRequests()或者http.formLogin()时所有规则都会被注册到HttpSecurity这个构建器内部最终在http.build()时转换成具体的过滤器。需要注意的一点是父类WebSecurityConfigurerAdapter的默认configure(HttpSecurity http)并不是空方法。在Spring Security 5.x里它大致做了这些事protected void configure(HttpSecurity http) throws Exception { http .csrf(withDefaults()) .exceptionHandling(withDefaults()) .headers(withDefaults()) .sessionManagement(withDefaults()) .securityContext(withDefaults()) .requestCache(withDefaults()) .anonymous(withDefaults()) .servletApi(withDefaults()) .logout(withDefaults()); }如果你在子类里重写configure(HttpSecurity http)之后没有调用super.configure(http)那么上面这些默认配置项全部不会生效。这意味着CSRF保护可能被关掉、匿名认证没了、默认登出路径也没了。很多人遇到“为什么我明明没动CSRF但它就是不拦截”这类问题根源往往就在这里。3.3 configure(WebSecurity)最容易被误解的顶层入口第三个configure(WebSecurity web)是三个方法里最少被重写的但它的作用域最顶层。它被调用的时机最晚——发生在WebSecurity构建器的configure阶段也就是上文中springSecurityFilterChain()方法里webSecurity.build()执行到后半段时。典型用法是这样的Override public void configure(WebSecurity web) { web.ignoring().antMatchers(/static/**, /favicon.ico); }很多人会拿configure(WebSecurity web)里的web.ignoring()和http.authorizeRequests().antMatchers(/static/**).permitAll()比觉得二者都是放行。但从源码角度看它们有本质区别经过web.ignoring()的路径请求根本不会进入Spring Security的FilterChainProxy属于“在安全框架的入口就被跳过”而经http.authorizeRequests().permitAll()的路径请求已经进入了过滤器链只是因为规则允许而不再拦截。前者更高效后者更灵活比如你还需要在静态资源上执行某些自定义过滤器逻辑时就只能用后者的方式。我用一个表格概括这三个方法方法调用时机作用对象典型场景configure(AuthenticationManagerBuilder)创建AuthenticationManager时认证管理器构建器注册UserDetailsService、PasswordEncoder、自定义AuthenticationProviderconfigure(HttpSecurity)构建单条过滤器链时HttpSecurity配置路径规则、表单登录、CSRF、Session策略configure(WebSecurity)顶层安全构建器配置阶段WebSecurity配置忽略路径、防火墙、调试开关4. HttpSecurity的构建过程getHttp()与DefaultSecurityFilterChain的诞生4.1 getHttp()方法内部到底做了什么getHttp()是WebSecurityConfigurerAdapter最核心的私有方法它负责创建并组装一个HttpSecurity实例。逻辑可以概括为如下几步private HttpSecurity getHttp() throws Exception { if (this.http ! null) { return this.http; } AuthenticationManager authenticationManager authenticationManager(); this.http new HttpSecurity(objectPostProcessor, authenticationManager, createSharedObjects()); configure(this.http); ... return this.http; }第一步调用authenticationManager()拿到认证管理器这个我们已经在3.1节说过。第二步把认证管理器、共享对象传给新创建的HttpSecurity。所谓“共享对象”包括UserDetailsService、SecurityContextHolder策略等这些对象会被后续配置的过滤器共享。第三步调用你重写的configure(HttpSecurity http)把你写的每一条规则注册进HttpSecurity。如果你设了断点跟踪这段流程会发现在getHttp()返回后HttpSecurity内部已经积累了大量配置信息但过滤器还没真正实例化。真正的实例化发生在http.build()被调用时那才是链上过滤器逐一诞生的时刻。4.2 过滤器链是如何被打包成DefaultSecurityFilterChain的DefaultSecurityFilterChain是FilterChainProxy内部用来描述“一条过滤器链”的数据结构。它持有两个关键信息一个RequestMatcher和一些过滤器。public final class DefaultSecurityFilterChain implements SecurityFilterChain { private final RequestMatcher requestMatcher; private final ListFilter filters; ... }requestMatcher决定这条链匹配哪些请求。如果你在configure(HttpSecurity http)里显式调用了http.antMatcher(/api/**)这里就会生成一个针对/api/**的匹配器。如果你没调用任何antMatcher或requestMatcher默认情况下会生成一个匹配所有请求的AnyRequestMatcher。这个“默认匹配所有请求”的行为正是多链配置时很多诡异问题的根源。filters就是这条链上实际会执行的过滤器列表。登录过滤器、匿名认证过滤器、Session过滤器、异常处理过滤器都在http.build()的过程中被实例化并加入这个列表。构建完成后这条DefaultSecurityFilterChain就成了一条独立的、自洽的过滤器链。4.3 多条链如何拼成顶层Filter当多个请求路径差异很大的业务模块共存在一个应用里时你可能会写多个继承WebSecurityConfigurerAdapter的配置类。每个配置类都会产生一个HttpSecurity进而生成一条DefaultSecurityFilterChain。这些链最终由WebSecurity在performBuild()阶段全部收集起来组装进一个FilterChainProxy。FilterChainProxy的请求分发逻辑很直接拿到请求后遍历内部所有SecurityFilterChain用各自的RequestMatcher去匹配第一个匹配上的链处理这个请求处理完就结束如果所有链都不匹配就继续走Servlet容器原有的过滤器链。一个很容易踩的坑是写了多个配置类但都没加Order注解也没有给链设置特定的antMatcher。这种情况下多个配置类默认都用AnyRequestMatcher第一条链就会匹配所有请求后面的链等于被“短路”永远没有机会执行。包含“放行静态资源”的配置类如果排在“拦截所有请求”的配置类后面那它的放行规则根本不会生效。5. 源码视角下最容易被坑到的三个典型问题5.1 重写configure(HttpSecurity)后默认安全配置哪里去了前文已经提到父类的默认configure(HttpSecurity http)注册了一批基础安全配置。如果你在子类中重写这个方法并写死自己的规则却不调用super.configure(http)这批默认配置就会整体消失。表现为CSRF防护失效、匿名认证对象不再自动注入、默认退出登录路径失效等。有些框架版本里默认的异常处理链、请求缓存配置也会因此全部丢失导致你遇到401/403时看到的响应体和原来完全不同。如果你确实需要完全自定义也不是不行但要知道自己在放弃什么。我个人的建议是除非你非常清楚这条链上该有哪些过滤器否则保留super.configure(http)调用再往上叠加自己的规则风险小很多。5.2 自定义UserDetailsService时AuthenticationManager为何不是你以为的那个我调试过一个很典型的案例在configure(AuthenticationManagerBuilder auth)里注册了自定义UserDetailsService断点确认这个方法确实被调用了但登录时用户名密码还是走了别的数据源。查了很久最后发现问题出在容器里同时存在一个独立的AuthenticationManagerBean并且某些配置分支没有走到WebSecurityConfigurerAdapter内部的局部认证管理器。回到authenticationManager()源码它的决策逻辑是局部构建器有配置就用局部的否则才用全局的。如果你的自定义UserDetailsService没有让localConfigureAuthenticationBldr.isConfigured()变为true比如只是注入了一个对象但没有调用auth.userDetailsService(...)那么框架还是会回退到容器级认证管理器。排查这类问题有几个很实用的手段第一在重写的configure(AuthenticationManagerBuilder auth)方法里打断点确认isConfigured()最终是否为true第二列出容器里所有AuthenticationManager类型的Bean看有没有多余定义第三在getHttp()里看传入HttpSecurity的authenticationManager实例和业务代码里用到的是不是同一个对象。5.3 为什么5.7之后新项目建议别再用它了如果你新建项目用的是Spring Security 5.7之后的版本会发现在IDE里继承WebSecurityConfigurerAdapter时类名上有一个删除线官方已经标记为Deprecated。到Spring Security 6.0这个类甚至被直接移除了。官方推荐的替代写法是组件化的SecurityFilterChainBeanBean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .csrf(csrf - csrf.disable()) .authorizeHttpRequests(auth - auth .requestMatchers(/public/**).permitAll() .anyRequest().authenticated() ) .formLogin(withDefaults()); return http.build(); }这种写法和WebSecurityConfigurerAdapter的底层机制其实是相通的HttpSecurity仍然是被传入的构建器你仍然是在配置规则最后仍然通过build()得到SecurityFilterChain。但不再需要继承不必受限于单一的扩展点配置类可以拆分成多个普通方法。从源码层面理解WebSecurityConfigurerAdapter的价值主要是为了读旧代码和弄清构建原理新项目直接用推荐写法会更省心。最后分享一个我在排查安全问题时最常用的调试技巧不要急着翻配置直接在WebSecurityConfiguration.springSecurityFilterChain()和AbstractConfiguredSecurityBuilder.doBuild()这两个方法里打断点看configurers列表里有哪些配置器、build()完成后返回的FilterChainProxy里到底装了几条链、每条链上有哪些过滤器。很多时候眼里的“配置不生效”其实只是配置对象根本没有被挂载到构建器上。把这条链路看清楚再去对照配置文件问题往往一眼就能定位。

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号