Spring Security 过滤 Web 请求全解析:从过滤器链到请求授权配置
Spring Security 对 Web 请求的过滤核心是 “代理过滤器 + 链式过滤” 机制:通过 DelegatingFilterProxy 衔接 Servlet 容器与 Spring 容器,再由 FilterChainProxy 管理具体的安全过滤链,最终通过 HttpSecurity 配置请求拦截规则。从 “过滤器链原理→自动注册机制→核心配置→请求授权规则” 四个维度,彻底讲透 Spring Security 如何过滤和保护 Web 请求。
核心过滤器链:DelegatingFilterProxy 与 FilterChainProxy
Spring Security 的过滤能力依赖两层代理机制,解决了 “Servlet Filter 由容器管理,而 Spring Security Filter 是 Spring Bean” 的协同问题。
1. DelegatingFilterProxy:Servlet Filter 与 Spring Bean 的桥梁
DelegatingFilterProxy 是 Servlet 规范中的 Filter 实现,但它本身不处理请求,仅负责将请求委托给 Spring 容器中的 FilterChainProxy Bean(默认 Bean 名为 springSecurityFilterChain)。
核心作用
- 解耦容器与 Spring:Servlet 容器(如 Tomcat)只能识别并管理
Filter实例,而 Spring Security 的过滤逻辑封装在 Spring Bean 中,DelegatingFilterProxy作为中间代理,实现两者通信; - 延迟查找目标 Bean:初始化时不直接依赖
FilterChainProxy,而是在请求到来时从 Spring 容器中查找,支持 Spring Bean 的懒加载。
自动注册机制(AbstractSecurityWebApplicationInitializer)
用户提供的代码中,SecurityWebApplicationInitializer 继承 AbstractSecurityWebApplicationInitializer,Spring 会自动检测该类,并在 Servlet 容器启动时注册 DelegatingFilterProxy:
1 | // AbstractSecurityWebApplicationInitializer 核心逻辑 |
- 无需手动配置 web.xml:即使不写 web.xml,Spring 也会通过
WebApplicationInitializer接口自动完成 Filter 注册,这是 Spring 3.1+ 推荐的无 XML 配置方式。
2. FilterChainProxy:Spring Security 的过滤链核心
springSecurityFilterChain 对应的实际类型是 FilterChainProxy,它是 Spring Security 内部的 “过滤链管理器”,本身是一个 Filter,但内部管理多个安全过滤链(如登录过滤链、授权过滤链、CSRF 过滤链等)。
核心特性
- 多链匹配:根据请求 URL 匹配不同的过滤链(例如,对
/api/**和/admin/**应用不同的安全规则); - 集中管理:所有 Spring Security 的 Filter(如
UsernamePasswordAuthenticationFilter、FilterSecurityInterceptor)都由它统一调度; - 与 Spring 集成:作为 Spring Bean,可依赖注入其他 Spring 组件(如
AuthenticationManager)。
如何被创建?(@EnableWebSecurity 的作用)
@EnableWebSecurity 注解会触发 WebSecurityConfiguration 配置类,其中会创建 FilterChainProxy 的 Bean:
1 | // WebSecurityConfiguration 核心代码 |
- @EnableWebSecurity 是开关:只有添加该注解,才会自动创建
FilterChainProxy,开启 Spring Security 的 Web 安全功能。
核心配置类:WebSecurityConfigurerAdapter
继承 WebSecurityConfigurerAdapter 是自定义 Spring Security 规则的主要方式,它提供三个核心 configure 方法,分别对应认证管理、Web 资源忽略、请求授权。
方法 1:configure (AuthenticationManagerBuilder auth) —— 配置认证来源
该方法用于定义 “如何获取用户信息”,是认证的基础,支持内存用户、数据库用户(自定义 UserDetailsService)等多种来源。
(1)内存用户(测试用)
1 |
|
密码加密注意:生产环境必须使用PasswordEncoder(如 BCrypt),不能用{noop},示例如下:
1
2
3
4
5
6
7
8
9
10
11
12
13// 配置 BCrypt 密码加密
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("tom")
.password(passwordEncoder().encode("123456")) // 加密密码
.roles("ADMIN");
}
(2)数据库用户(生产用)
通过自定义 UserDetailsService 从数据库获取用户信息,这是生产环境的标准用法:
1 | // 1. 自定义 UserDetailsService(从数据库查询用户) |
方法 2:configure (WebSecurity web) —— 忽略静态资源
该方法用于配置 “无需安全过滤的资源”(如静态文件、Swagger 文档),避免 Spring Security 拦截这些请求,提升性能。
示例:忽略静态资源和 Swagger 路径
1 |
|
- 注意区别:
WebSecurity.ignoring()是 “完全忽略”,这些请求不会经过任何 Spring Security Filter;而HttpSecurity.authorizeRequests().permitAll()是 “允许匿名访问”,请求仍会经过过滤链,但无需认证。
方法 3:configure (HttpSecurity http) —— 配置请求授权与登录退出
这是最核心的配置方法,用于定义 “哪些请求需要保护”“如何登录”“如何退出”“异常处理” 等规则,用户提供的代码中包含大量配置样例,下面逐一解析核心配置项。
(1)登录配置(formLogin ())
配置自定义登录页面、登录参数、登录成功 / 失败跳转:
1 | http.formLogin() |
- 关键注意:
loginPage("/login.html")会改变默认的登录相关路径,需确保/login.html、/user/login这些路径允许匿名访问(通过permitAll())。
(2)退出配置(logout ())
配置退出登录的路径、成功跳转和资源清理:
1 | http.logout() |
(3)请求授权规则(authorizeRequests ())
定义不同请求路径的访问权限,规则匹配按顺序执行,更具体的规则需放在前面:
1 | http.authorizeRequests() |
| 权限规则方法 | 作用 | 示例 |
|---|---|---|
permitAll() |
允许匿名访问 | .antMatchers("/login").permitAll() |
authenticated() |
需已认证(登录) | .anyRequest().authenticated() |
hasAuthority() |
需具备指定权限(无前缀) | .hasAuthority("USER_UPDATE") |
hasAnyAuthority() |
需具备任意一个指定权限 | .hasAnyAuthority("ADMIN", "USER") |
hasRole() |
需具备指定角色(自动加 ROLE_ 前缀) | .hasRole("admin") → 匹配 ROLE_ADMIN |
hasAnyRole() |
需具备任意一个指定角色 | .hasAnyRole("admin", "teacher") |
hasIpAddress() |
需来自指定 IP 或 IP 段 | .hasIpAddress("192.168.1.0/24") |
rememberMe() |
需通过 “记住我” 认证 | .antMatchers("/remember").rememberMe() |
denyAll() |
拒绝所有访问 | .antMatchers("/secret").denyAll() |
(4)其他常用配置
CSRF 保护:默认开启,防止跨站请求伪造,前后端分离项目可关闭:
1
http.csrf().disable(); // 关闭 CSRF 保护(前后端分离常用)
记住我(Remember Me):基于 Cookie 存储认证信息,无需每次登录:
1
2
3
4http.rememberMe()
.tokenValiditySeconds(3600) // Cookie 有效期(秒)
.key("my-secret-key") // 加密 Cookie 的密钥(生产环境需复杂密钥)
.userDetailsService(customUserDetailsService); // 用于加载用户信息异常处理:配置无权限访问时的跳转页面:
1
2http.exceptionHandling()
.accessDeniedPage("/unauth.html"); // 403 无权限时跳转的页面HTTPS 强制:要求所有请求通过 HTTPS 访问:
1
2http.requiresChannel()
.anyRequest().requiresSecure(); // 所有请求强制 HTTPS
Web 请求过滤的完整流程
结合上述组件,一个 Web 请求的安全过滤流程如下:
- 请求到达 Servlet 容器:浏览器发送请求(如
/admin/user); - DelegatingFilterProxy 拦截:转发请求给 Spring 容器中的
FilterChainProxy; - FilterChainProxy 匹配过滤链:根据请求路径(
/admin/**)匹配对应的安全过滤链; - 执行过滤链中的 Filter:
- 登录相关 Filter(如
UsernamePasswordAuthenticationFilter):处理登录请求; - 授权 Filter(如
FilterSecurityInterceptor):检查请求是否有权限; - CSRF Filter(如
CsrfFilter):验证 CSRF Token;
- 登录相关 Filter(如
- 权限校验:
FilterSecurityInterceptor调用AuthenticationManager校验用户权限,通过则放行请求,否则返回 403 或跳转登录页; - 请求到达 Controller:安全校验通过后,请求最终转发到 Spring MVC 的 Controller 处理。
关键注意事项
- 规则匹配顺序:
authorizeRequests()中的规则按代码顺序执行,更具体的规则必须放在前面,否则会被后面的通用规则覆盖(例如,/admin/**需放在anyRequest()之前); - 密码加密:生产环境必须使用
PasswordEncoder(如 BCrypt、Argon2),禁止使用{noop}明文存储; - 静态资源忽略:通过
WebSecurity.ignoring()忽略静态资源,避免过滤链处理静态文件,提升性能; - 前后端分离适配:前后端分离项目需关闭 CSRF(
csrf().disable()),并配置loginProcessingUrl为接口路径(如/api/login),返回 JSON 结果而非跳转页面。
总结
Spring Security 过滤 Web 请求的核心是 “代理转发 + 链式规则”:
- DelegatingFilterProxy:衔接 Servlet 容器与 Spring 容器,解决 Filter 管理边界问题;
- FilterChainProxy:管理内部安全过滤链,按请求匹配执行对应的安全逻辑;
- WebSecurityConfigurerAdapter:通过三个
configure方法,分别配置认证来源、资源忽略、请求授权,实现灵活的安全规则定义