Spring Security - FilterChainProxy

1️⃣ DelegatingFilterProxy

  1. 属于 Spring Web(Servlet 规范 + Spring Framework) 的类,不是 Spring Security 专属, 它是连接 Servlet 容器中的 FilterSpring 容器中的 Bean 的关键桥梁。

  2. 它本身是一个标准的 jakarta.servlet.Filter,但是并不直接做实际的过滤逻辑,而是 把请求委托给 Spring 容器里定义的某个 Bean(通常是一个 Filter 类型的 Bean) 去执行(如 FilterChainProxy)去执行。


存在的意义

  • Servlet 容器(如 Tomcat)在启动时,会读取 web.xml(或 ServletContext 注册的 Filter),只能直接加载类实例,无法感知 Spring 容器里的 Bean
  • 而 Spring Security 的核心是 一条 Filter 链(FilterChainProxy),必须交由 Spring 容器管理(便于依赖注入、AOP、配置)。
  • 所以:需要 DelegatingFilterProxy 作为适配器,把 Servlet 容器里的 Filter 请求转发给 Spring 容器里的目标 Bean。

简单说: Tomcat → 调用 DelegatingFilterProxy → 找到 Spring 容器中的 bean → 委托执行。


工作机制

  1. Servlet 容器启动时,DelegatingFilterProxy 被注册为 Filter。

  2. 它会从 Spring 容器里找到与自己名字(或配置的名字)相同的 Bean(通常是 FilterChainProxy)。

  3. 每次请求进来时:

    • DelegatingFilterProxy#doFilter(...) 会调用目标 Bean 的 doFilter(...)
    • 实际逻辑由 Spring 管理的 Bean 来完成。
java 复制代码
public class DelegatingFilterProxy extends GenericFilterBean {

    private String targetBeanName;
    private WebApplicationContext webApplicationContext;
    private volatile Filter delegate;

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
        throws ServletException, IOException {
        
        // 初始化目标 Bean(只做一次)initFilterBean -> this.delegate = this.initDelegate(wac);
		Filter delegateToUse = this.delegate;
        
        // 委托目标 Bean 执行过滤逻辑
        delegateToUse.doFilter(request, response, filterChain);
    }
}

2️⃣ FilterChainProxy

  • 角色:Spring Security 的核心入口 Filter。

  • 职责

    1. 接收请求(由 DelegatingFilterProxy 转发)。
    2. 内部维护一个 SecurityFilterChain 列表 ,根据 URL 匹配 SecurityFilterChain ,然后依次执行里面的 Security Filter;Spring Security 的总调度器

FilterChainProxy 是如何被调用的

  1. Servlet 容器(Tomcat)收到请求。
  2. Filter 链 执行到 DelegatingFilterProxy
  3. DelegatingFilterProxy#doFilter()
java 复制代码
Filter delegate = getDelegate(); // 获取 springSecurityFilterChain Bean
delegate.doFilter(request, response, filterChain);
  1. delegate 就是 FilterChainProxy 实例。

  2. 所以 FilterChainProxy#doFilter() 被 Servlet 容器调用,传入原始 FilterChain(继续调用 DispatcherServlet 或下一个 Filter)。

FilterChainProxy 内部逻辑

java 复制代码
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException { 
	FirewalledRequest firewallRequest = this.firewall.getFirewalledRequest((HttpServletRequest) request);
	HttpServletResponse firewallResponse = this.firewall.getFirewalledResponse((HttpServletResponse) response);
    // 找到匹配的 SecurityFilterChain  
	List<Filter> filters = null;
	// 多条 SecurityFilterChain 如何匹配
	for (SecurityFilterChain chain : this.filterChains) {
		if (chain.matches(request)) {
			// 默认只执行第一个匹配的; 所以在配置多个链时,顺序很重要,URL 模式越具体的链应该放前面。
			filters = chain.getFilters();
			break;
		} 
	}

	// 没匹配到 -> 直接放行
    if (filters == null || filters.size() == 0) { 
		this.filterChainDecorator.decorate(chain).doFilter(firewallRequest, firewallResponse);
		return;
	}
		
    // 如果匹配: 
    // 创建"重置链",用于恢复请求状态并调用原始 FilterChain 
	FilterChain reset = (req, res) -> {
	    // 恢复请求状态,清理 Security Filter 改动。
		firewallRequest.reset(); 
		chain.doFilter(req, res); // 调用原始 FilterChain, 其实就是 Servlet 容器原本的 FilterChain(最终会到 DispatcherServlet)。
	};
	// 创建 VirtualFilterChain,执行 Filters, 最后调用 reset
	this.filterChainDecorator.decorate(reset, filters).doFilter(firewallRequest, firewallResponse); 
}

SecurityFilterChain 的匹配

  1. SecurityFilterChain 是一个接口,包含一组 Spring Security 的内置 Filter 和 对应一个 URL 配置模式。
java 复制代码
public interface SecurityFilterChain {
    boolean matches(HttpServletRequest request);
    List<Filter> getFilters();
}
  1. FilterChainProxy 会遍历所有链,使用 RequestMatcher 判断请求是否匹配, 找到第一个匹配的链执行。

    • matches() 通常是 DefaultSecurityFilterChain 实现,内部使用 RequestMatcher 判断 URL 是否匹配:
    java 复制代码
    public boolean matches(HttpServletRequest request) {
        return this.requestMatcher.matches(request);
    }

可以有多个 SecurityFilterChain(多安全配置),比如:

  • /api/** → JWT 认证的过滤器链
  • /admin/** → Session 登录的过滤器链

FilterChainProxy 调用 Filter 的方式

  • 通过 VirtualFilterChain 封装原来的 FilterChain。
  • 核心是递归调用
java 复制代码
private static class VirtualFilterChain implements FilterChain {
	private final FilterChain originalChain;// FilterChain resert
	private final List<Filter> additionalFilters; // 这一条 SecurityFilterChain 内的所有 Security Filter
	private final int size; // Filter 数量
	private int currentPosition = 0; // 当前执行到第几个 Filter

    public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
        // 递归终止条件:如果已经执行完 additionalFilters 中的所有 Filter,调用 原始 FilterChain
		// 保证请求最终能到 DispatcherServlet 或原始 Filter 链。
		if (currentPosition == additionalFilters.size()) {
            // 所有 Security Filter 执行完后 -> 继续执行原始 FilterChain,保证请求最终能到达 DispatcherServlet。
            originalChain.doFilter(request, response);
			return;
        }
		this.currentPosition++;
		// 取出当前 Filter
		Filter nextFilter = this.additionalFilters.get(this.currentPosition - 1);
		// 调用它的 doFilter, 把 VirtualFilterChain 自身 (this) 作为下一个 FilterChain 传入
		nextFilter.doFilter(request, response, this); // 递归
    }
}
  1. 顺序执行

    • Security Filter 的执行顺序严格按照 additionalFilters 列表的顺序。
    • 这是为什么 Spring Security 中不同 Filter 的顺序很重要(例如 ExceptionTranslationFilter 要在 UsernamePasswordAuthenticationFilter 前面)。
    • 若执行过程中抛出认证或授权异常(如 AuthenticationException),会被 ExceptionTranslationFilter 捕获处理,不会继续执行后续 Filter。
  2. 递归链

    • VirtualFilterChain 本质上是 递归调用链 ,每个 Filter 完成后调用 chain.doFilter(),进入下一层 Filter。
  3. 最终回原始链

    • currentPosition == size,执行 originalChain.doFilter(),把请求交回原始的 Servlet FilterChain(进入 DispatcherServlet)。

3️⃣ 补充,总结

DelegatingFilterProxy 的职责

  • 作为 Servlet Filter 注册到 Servlet 容器中,但本身不实现安全逻辑
  • 将请求委托给 Spring 容器中名为 springSecurityFilterChain 的 Bean(即 FilterChainProxy)。
  • 默认情况下,Spring Security 会把 FilterChainProxy 注册为一个 名为 "springSecurityFilterChain" 的 Bean

FilterChainProxy 的创建与注册

  1. Spring Security 配置
java 复制代码
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        // HttpSecurity DSL 配置生成 SecurityFilterChain
        return http.build();
    }
}
  • 底层过程

    • @EnableWebSecurity 会触发 WebSecurityConfiguration 自动配置,注册 springSecurityFilterChain Bean
    • 类型就是 FilterChainProxy
    • FilterChainProxy 的构造器里拿到所有 SecurityFilterChain

  1. Spring Bean 注册
java 复制代码
@Bean(name = "springSecurityFilterChain")
public FilterChainProxy springSecurityFilterChain(List<SecurityFilterChain> chains) {
    return new FilterChainProxy(chains);
}
  • 这个 Bean 就是 DelegatingFilterProxy 要委托的目标。

DelegatingFilterProxy 调用流程

假设你在 web.xml 或 Spring Boot 默认注册了(一般就自动配置):

java 复制代码
<filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
  • Tomcat / Servlet 容器启动:

    • 扫描到 DelegatingFilterProxy
    • 调用 DelegatingFilterProxy.init()

DelegatingFilterProxy init() 核心源码逻辑

java 复制代码
@Override
public void init(FilterConfig filterConfig) {
    // 1. Bean 名称,默认为 filter-name
    String targetBeanName = getTargetBeanName();

    // 2. 从 Spring WebApplicationContext 获取 Bean
    this.delegate = obtainWebApplicationContext(filterConfig)
                        .getBean(targetBeanName, Filter.class);

    // delegate 就是 FilterChainProxy 实例
}
  • delegate 就是 FilterChainProxy Bean
  • 之后所有请求都委托给这个 Bean:
java 复制代码
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws ServletException, IOException {
    this.delegate.doFilter(request, response, chain); // ✅ 这里调用 FilterChainProxy.doFilter()
}

调用流程图

scss 复制代码
┌───────────────────────────────┐
│           客户端请求           │
└───────────────┬───────────────┘
                │
                ▼
       Tomcat / Servlet 容器 Filter 链
                │
                ▼
 ┌───────────────────────────────┐
 │ DelegatingFilterProxy (Filter) │
 │ init(): 获取 delegate Bean      │
 │ delegate = springSecurityFilterChain (FilterChainProxy) │
 └───────────────┬───────────────┘
                 │ doFilter(request, response, chain)
                 ▼
        ┌─────────────────────────┐
        │   FilterChainProxy      │
        │ doFilterInternal():    │
        │ 1️⃣ Firewall 防护       │
        │ 2️⃣ 匹配 SecurityFilterChain │
        │ 3️⃣ 创建 VirtualFilterChain │
        │ 4️⃣ 执行 Security Filter 链 │
        └───────────────┬─────────┘
                        │
                        ▼
        ┌─────────────────────────┐
        │ VirtualFilterChain      │
        │(递归执行每个 Filter) │
        │ ┌───────────────┐     │
        │ │ SecurityFilter1│ --> chain.doFilter(this)
        │ ├───────────────┤
        │ │ SecurityFilter2│ --> chain.doFilter(this)
        │ └───────────────┘
        │
        │ 当 currentPosition == size
        │ 执行 reset 链
        ▼
┌───────────────────────────────┐
│ FilterChain reset (lambda)    │
│ firewallRequest.reset()       │
│ originalChain.doFilter()      │ <-- 调用原始 FilterChain
└───────────────┬───────────────┘
                │
                ▼
      ┌──────────────────────────┐
      │ DispatcherServlet / MVC  │
      │ Controller 业务处理       │
      └──────────────────────────┘

reset 链

  • 恢复请求状态(firewallRequest.reset()); reset 链就是保证 请求能回到原始处理链
  • 调用原始 FilterChain.doFilter() → DispatcherServlet

SecurityFilterChain

  • 包含一组 Security Filter + URL 匹配规则
  • FilterChainProxy 会找到第一个匹配的链并执行

original FilterChain / DispatcherServlet

  • Security 处理完毕后,请求继续执行原始链
  • 最终到达 Controller / MVC 逻辑

相关推荐
SimonKing6 小时前
Apache Commons Math3 使用指南:强大的Java数学库
java·后端·程序员
渣哥6 小时前
Java 集合迭代中的 fail-fast 与 fail-safe 机制详解
java
帧栈6 小时前
我的创作纪念日
java
bug攻城狮6 小时前
Spring Boot Banner
java·spring boot·后端
黑马源码库miui520867 小时前
JAVA同城打车小程序APP打车顺风车滴滴车跑腿源码微信小程序打车源码
java·微信·微信小程序·小程序·uni-app
MadPrinter7 小时前
SpringBoot学习日记 Day11:博客系统核心功能深度开发
java·spring boot·后端·学习·spring·mybatis
淦出一番成就7 小时前
Java反序列化接收多种格式日期-JsonDeserialize
java·后端
Java中文社群7 小时前
Hutool被卖半年多了,现状是逆袭还是沉寂?
java·后端
爱吃苹果的日记本8 小时前
开学第一课
java