DelegatingFilterProxy 分析

本文深入解析了DelegatingFilterProxy类在Spring Security中的内部运行机制,阐述了其作为代理模式的应用如何将Servlet容器中的filter与Spring容器中的bean关联起来。重点介绍了初始化流程、FilterChainProxy实例的获取以及最终如何响应请求的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
        Filter delegate = wac.getBean(getTargetBeanName(), Filter.class);
        if (isTargetFilterLifecycle()) {
            delegate.init(getFilterConfig());
        }
        return delegate;
}
 使用过springSecurity的朋友都知道,首先需要在web.xml进行以下配置,

 

 <filter>
  <description>登录验证,访问权限控制。</description>
  <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>

 从这个配置中,可能会给我们造成一个错觉,以为DelegatingFilterProxy类就是springSecurity的入口,但其实这个类位于spring-web-3.0.5.RELEASE.jar这个jar下面,说明这个类本身是和springSecurity无关。DelegatingFilterProxy类继承于抽象类GenericFilterBean,间接地implement 了javax.servlet.Filter接口,Servlet容器在启动时,首先会调用Filter的init方法,GenericFilterBean的作用主要是可以把Filter的初始化参数自动地set到继承于GenericFilterBean类的Filter中去。在其init方法的如下代码就是做了这个事:

public final void init(FilterConfig filterConfig) throws ServletException {
		Assert.notNull(filterConfig, "FilterConfig must not be null");
		if (logger.isDebugEnabled()) {
			logger.debug("Initializing filter '" + filterConfig.getFilterName() + "'");
		}

		this.filterConfig = filterConfig;

		// Set bean properties from init parameters.
		try {
			PropertyValues pvs = new FilterConfigPropertyValues(filterConfig, this.requiredProperties);
			BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
			ResourceLoader resourceLoader = new ServletContextResourceLoader(filterConfig.getServletContext());
			bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.environment));
			initBeanWrapper(bw);
			bw.setPropertyValues(pvs, true);
		}
		catch (BeansException ex) {
			String msg = "Failed to set bean properties on filter '" +
			    filterConfig.getFilterName() + "': " + ex.getMessage();
			logger.error(msg, ex);
			throw new NestedServletException(msg, ex);
		}

		// 这个方法留给子类来扩展
		initFilterBean();

		if (logger.isDebugEnabled()) {
			logger.debug("Filter '" + filterConfig.getFilterName() + "' configured successfully");
		}
	}

 DelegatingFilterProxy  initFilterBean方法的代码如下:

 

protected void initFilterBean() throws ServletException {
        // If no target bean name specified, use filter name.
        if (this.targetBeanName == null) {
            this.targetBeanName = getFilterName();
        }
 
        // Fetch Spring root application context and initialize the delegate early,
        // if possible. If the root application context will be started after this
        // filter proxy, we'll have to resort to lazy initialization.
        synchronized (this.delegateMonitor) {
            WebApplicationContext wac = findWebApplicationContext();
            if (wac != null) {
                this.delegate = initDelegate(wac);
            }
        }
    }
 可以看出上述代码首先看Filter是否提供了targetBeanName初始化参数,如果没有提供则直接使用filter的name做为beanName,产生了beanName后,由于我们在web.xml的filter的name是springSecurityFilterChain,从spring的IOC容器中取出bean的代码是initDelegate方法,下面是该方法代码
protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
        Filter delegate = wac.getBean(getTargetBeanName(), Filter.class);
        if (isTargetFilterLifecycle()) {
            delegate.init(getFilterConfig());
        }
        return delegate;
}
 

 

通过跟踪代码,发现取出的bean是org.springframework.security.FilterChainProxy,该类也是继承于GenericFilterBean,取出bean后,判断targetFilterLifecycle属性是false还是true,决定是否调用该类的init方法。这个FilterChainProxy bean实例最终被保存在DelegatingFilterProxy类的delegate属性里

下面看一下DelegatingFilterProxy类的doFilter方法

public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
 
        // Lazily initialize the delegate if necessary.
        Filter delegateToUse = null;
        synchronized (this.delegateMonitor) {
            if (this.delegate == null) {
                WebApplicationContext wac = findWebApplicationContext();
                if (wac == null) {
                    throw new IllegalStateException("No WebApplicationContext found: no ContextLoaderListener registered?");
                }
                this.delegate = initDelegate(wac);
            }
            delegateToUse = this.delegate;
        }
 
        // Let the delegate perform the actual doFilter operation.
        invokeDelegate(delegateToUse, request, response, filterChain);
    }

 真正要关注invokeDelegate(delegateToUse, request, response, filterChain);这句代码,在下面可以看出DelegatingFilterProxy类实际是用其delegate属性即org.springframework.security.FilterChainProxy实例的doFilter方法来响应请求

protected void invokeDelegate(
            Filter delegate, ServletRequest request, ServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
 
        delegate.doFilter(request, response, filterChain);
    }

 以上就是DelegatingFilterProxy类的一些内部运行机制,其实主要作用就是一个代理模式的应用,可以把servlet 容器中的filter同spring容器中的bean关联起来

### Spring Security 源码详细解析 #### 一、Spring Security 的核心架构 Spring Security 是一个强大的安全框架,其设计目标是提供全面的安全解决方案。它的主要功能包括身份验证(Authentication)、授权(Authorization)以及保护应用程序免受各种攻击模式的影响。 Spring Security 并不依赖于 Spring MVC 或其他特定的 Web 技术栈,而是直接与 Servlet 容器集成[^1]。这种集成的核心机制是一个名为 `DelegatingFilterProxy` 的过滤器,该过滤器负责将所有的 HTTP 请求传递给 Spring Security 中定义的一系列过滤器链(Filter Chain)。这些过滤器共同协作完成诸如登录、注销、权限校验等功能。 --- #### 二、Spring Security 过滤器链的工作原理 Spring Security 使用了一种基于过滤器的设计模式来处理安全性需求。整个流程由多个过滤器组成,每个过滤器专注于解决某一方面的安全问题。以下是典型的过滤器及其职责: 1. **ChannelProcessingFilter**: 负责强制执行通道约束(例如 HTTPS),确保某些 URL 只能通过指定协议访问。 2. **SecurityContextPersistenceFilter**: 初始化或恢复当前线程中的 `SecurityContextHolder` 对象,用于存储用户的认证信息。 3. **LogoutFilter**: 处理用户登出逻辑。 4. **UsernamePasswordAuthenticationFilter**: 实现表单登录的身份验证。 5. **BasicAuthenticationFilter**: 提供 Basic Authentication 支持。 6. **ExceptionTranslationFilter**: 将异常转换为相应的 HTTP 响应状态码。 7. **FilterSecurityInterceptor**: 执行方法级和 URL 级别的访问控制决策。 8. **SwitchUserFilter**: 允许管理员切换到另一个用户账户下操作。 以上提到的每一个过滤器都按照既定顺序被加载并串联起来形成完整的 Filter Chain[^1]。 --- #### 三、如何初始化过滤器链? 在 Spring Boot 应用程序中,默认情况下会自动生成一条基础版的过滤器链条——即所谓的 `springSecurityFilterChain` Bean。这个 Bean 是通过 `SpringBootWebSecurityConfiguration` 类配置出来的[^2]。下面展示了一个简化版本的例子说明这一过程是如何发生的: ```java @Configuration(proxyBeanMethods = false) @ConditionalOnDefaultWebSecurity @ConditionalOnWebApplication(type = Type.SERVLET) public class SpringBootWebSecurityConfiguration { @Bean @Order(SecurityProperties.BASIC_AUTH_ORDER) public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception { http.authorizeRequests() .anyRequest().authenticated() // 配置所有请求均需经过身份验证 .and() .formLogin() // 启用 Form 登录方式 .and() .httpBasic(); // 启用 Http Basic 认证 (较少使用) return http.build(); } } ``` 上述代码片段展示了 Spring Boot 如何利用 Java Config 来构建默认的安全策略:任何未匹配具体路径规则的请求都将受到严格管控,只有已成功登陆过的客户端才允许继续浏览资源[^2]。 --- #### 四、定制化安全设置 尽管存在开箱即用的基础防护措施,但在实际项目开发过程中往往还需要针对业务场景做出进一步调整优化。这通常涉及以下几个方面: - 添加额外的拦截条件; - 修改现有行为以适配特殊需求; - 替换内置组件实例以便引入第三方扩展模块等。 要实现这一点,则需要继承抽象基类 AbstractSecurityConfigurerAdapter 或者直接重写 configure 方法来自定义全局属性设定项。比如我们可以这样覆盖掉原始的 UserDetailsService 接口实现从而支持更复杂的密码加密算法或者外部 OAuth2/OIDC 协议对接服务等等。 --- #### 五、总结 通过对 Spring Security 源码的学习可以看出,它不仅提供了丰富的 API 和工具集帮助开发者快速搭建起一套完善的企业级安全保障体系;而且由于采用了高度灵活可插拔式的结构设计理念,使得即使面对复杂多变的实际应用场景也能够轻松应对自如。无论是初学者还是资深工程师都可以从中受益匪浅! ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值