过滤器链加载原理

简介: 通过分析DelegatingFilterProxy、FilterChainProxy与SecurityFilterChain源码,揭示了Spring Security过滤器链的加载机制:由web.xml中配置的DelegatingFilterProxy代理,通过bean名称获取FilterChainProxy实例,再由其内部封装的SecurityFilterChain加载15个安全过滤器,最终构建完整的安全过滤链。

通过前面十五个过滤器功能的介绍,对于SpringSecurity简单入门中的疑惑是不是在心中已经有了答案了呀? 但新的问题来了!我们并没有配置这些过滤器啊?它们都是怎么被加载出来的?
1-DelegatingFilterProxy
我们在web.xml中配置了一个名称为springSecurityFilterChain的过滤器DelegatingFilterProxy,接下我直接对 DelegatingFilterProxy源码里重要代码进行说明,其中删减掉了一些不重要的代码,大家注意我写的注释就行了!
public class DelegatingFilterProxy extends GenericFilterBean {
@Nullable
private String contextAttribute;
@Nullable
private WebApplicationContext webApplicationContext;
@Nullable
private String targetBeanName;
private boolean targetFilterLifecycle;
@Nullable
private volatile Filter delegate;//注:这个过滤器才是真正加载的过滤器
private final Object delegateMonitor;
//注:doFilter才是过滤器的入口,直接从这看!
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain
filterChain) throws ServletException, IOException {
Filter delegateToUse = this.delegate;
if (delegateToUse == null) {
synchronized(this.delegateMonitor) {
delegateToUse = this.delegate;
if (delegateToUse == null) {
WebApplicationContext wac = this.findWebApplicationContext();
if (wac == null) {
throw new IllegalStateException("No WebApplicationContext found: no
ContextLoaderListener or DispatcherServlet registered?");
}
//第一步:doFilter中最重要的一步,初始化上面私有过滤器属性delegate
delegateToUse = this.initDelegate(wac);
}
this.delegate = delegateToUse;
}
}
//第三步:执行FilterChainProxy过滤器
this.invokeDelegate(delegateToUse, request, response, filterChain);
}
//第二步:直接看最终加载的过滤器到底是谁
protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
//debug得知targetBeanName为:springSecurityFilterChain
String targetBeanName = this.getTargetBeanName();
Assert.state(targetBeanName != null, "No target bean name set");
//debug得知delegate对象为:FilterChainProxy
Filter delegate = (Filter)wac.getBean(targetBeanName, Filter.class);
if (this.isTargetFilterLifecycle()) {
delegate.init(this.getFilterConfig());
}
return delegate;
}
}
第二步debug结果如下:

由此可知,DelegatingFilterProxy通过springSecurityFilterChain这个名称,得到了一个FilterChainProxy过滤器, 最终在第三步执行了这个过滤器。
2-FilterChainProxy
注意代码注释!
public class FilterChainProxy extends GenericFilterBean {
private static final Log logger = LogFactory.getLog(FilterChainProxy.class);
private static final String FILTER_APPLIED =
FilterChainProxy.class.getName().concat(".APPLIED");
private List filterChains;
private FilterChainProxy.FilterChainValidator filterChainValidator;
private HttpFirewall firewall;
//咿!?可以通过一个叫SecurityFilterChain的对象实例化出一个FilterChainProxy对象
//这FilterChainProxy又是何方神圣?会不会是真正的过滤器链对象呢?先留着这个疑问!
public FilterChainProxy(SecurityFilterChain chain) {
this(Arrays.asList(chain));
}
//又是SecurityFilterChain这家伙!嫌疑更大了!
public FilterChainProxy(List filterChains) {
this.filterChainValidator = new FilterChainProxy.NullFilterChainValidator();
this.firewall = new StrictHttpFirewall();
this.filterChains = filterChains;
}
//注:直接从doFilter看
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
boolean clearContext = request.getAttribute(FILTER_APPLIED) == null;
if (clearContext) {
try {
request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
this.doFilterInternal(request, response, chain);
} finally {
SecurityContextHolder.clearContext();
request.removeAttribute(FILTER_APPLIED);
}
} else {
//第一步:具体操作调用下面的doFilterInternal方法了
this.doFilterInternal(request, response, chain);
}
}
private void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain
chain) throws IOException, ServletException {
FirewalledRequest fwRequest =
this.firewall.getFirewalledRequest((HttpServletRequest)request);
HttpServletResponse fwResponse =
this.firewall.getFirewalledResponse((HttpServletResponse)response);
//第二步:封装要执行的过滤器链,那么多过滤器就在这里被封装进去了!
List filters = this.getFilters((HttpServletRequest)fwRequest);
if (filters != null && filters.size() != 0) {
FilterChainProxy.VirtualFilterChain vfc = new
FilterChainProxy.VirtualFilterChain(fwRequest, chain, filters);
//第四步:加载过滤器链
vfc.doFilter(fwRequest, fwResponse);
} else {
if (logger.isDebugEnabled()) {
logger.debug(UrlUtils.buildRequestUrl(fwRequest)

                         + (filters == null ? " has no matching filters" : " has an empty filter list"));
        }
        fwRequest.reset();
        chain.doFilter(fwRequest, fwResponse);
    }
}
private List<Filter> getFilters(HttpServletRequest request) {
    Iterator var2 = this.filterChains.iterator();
    //第三步:封装过滤器链到SecurityFilterChain中!
    SecurityFilterChain chain;
    do {
        if (!var2.hasNext()) {
            return null;
        }
            chain = (SecurityFilterChain)var2.next();
    } while(!chain.matches(request));
    return chain.getFilters();
}

}
第二步debug结果如下图所示,惊不惊喜?十五个过滤器都在这里了!

再看第三步,怀疑这么久!原来这些过滤器还真是都被封装进SecurityFilterChain中了。
3-SecurityFilterChain
最后看SecurityFilterChain,这是个接口,实现类也只有一个,这才是web.xml中配置的过滤器链对象!
//接口
public interface SecurityFilterChain {
boolean matches(HttpServletRequest var1);
List getFilters();
}

//实现类
public final class DefaultSecurityFilterChain implements SecurityFilterChain {

private static final Log logger = LogFactory.getLog(DefaultSecurityFilterChain.class);
private final RequestMatcher requestMatcher;
private final List<Filter> filters;

public DefaultSecurityFilterChain(RequestMatcher requestMatcher,
                                  Filter... filters) {
    this(requestMatcher, Arrays.asList(filters));
}

public DefaultSecurityFilterChain(RequestMatcher requestMatcher, 
                                  List<Filter> filters) {
    logger.info("Creating filter chain: " + requestMatcher + ", " + filters);
    this.requestMatcher = requestMatcher;
    this.filters = new ArrayList(filters);
}

public RequestMatcher getRequestMatcher() {
    return this.requestMatcher;
}

public List<Filter> getFilters() {
    return this.filters;
}

public boolean matches(HttpServletRequest request) {
    return this.requestMatcher.matches(request);
}

public String toString() {
    return "[ " + this.requestMatcher + ", " + this.filters + "]";
}

}
总结:通过此章节,我们对SpringSecurity工作原理有了一定的认识。但理论千万条,功能第一条,探寻底层,是为了更好的使用框架。 那么,言归正传!到底如何使用自己的页面来实现SpringSecurity的认证操作呢?要完成此功能,首先要有一套自己的页面!

相关文章
|
4月前
|
机器学习/深度学习 人工智能 监控
构建AI智能体:六十五、模型智能训练控制:早停机制在深度学习中的应用解析
文章摘要:早停机制是深度学习中防止过拟合的关键技术,通过在验证集性能停止改善时终止训练,自动平衡模型复杂度和泛化能力。其核心价值包括自动防过拟合、提升训练效率(节省30-80%计算资源)、简化调参过程。关键参数设置涉及patience(容忍轮次)、min_delta(最小改善阈值)和restore_best_weights(恢复最佳权重)。实现流程包括训练轮次监控、验证集评估和性能改善判断,通过U型曲线分析可直观理解其工作原理。
536 20
|
4月前
|
数据采集 人工智能 安全
|
4月前
|
存储 弹性计算 安全
阿里云“99套餐”活动解析:套餐相关规则及云产品及组合配置与价格参考
阿里云“99套餐”是阿里云为了助力个人和中小企业无忧上云而推出的套餐组合活动,目前活动截止日期已延长到2027年3月31日。“99套餐”为在选购云服务器的同时还需要选购AI建站、SSL证书、安全防护包、RDS数据库等云产品的用户提供了额外的组合购买套餐,包括建站礼包、加36元防护主机安全、加99元解锁弹性数据库、加99元享高效存储保障等套餐,帮助用户一键选购。本文为大家介绍活动的相关规则及组合套餐配置与价格,以供参考。
595 2
|
9天前
|
人工智能 机器人
阿里大动作!CEO 亲自挂帅成立 Token Hub 事业群,押注 AGI 时代新赛道
阿里成立CEO吴泳铭亲掌的Alibaba Token Hub(ATH)事业群,以“Token”为战略核心,整合通义实验室、MaaS、千问、悟空及AI创新五大板块,构建“创造—输送—应用”全链路AI生态。首次亮相的悟空事业部聚焦B端AI原生工作平台,协同C端千问,打造双轮驱动商业化闭环,剑指AGI时代核心生产要素之争。(239字)
365 6
|
4月前
|
前端开发 程序员
常见注解及使用说明
本文介绍了SpringMVC中@RequestMapping注解的作用及原理,它用于将HTTP请求映射到控制器方法,实现前后端接口路径对应。并通过@GetMapping等派生注解简化开发,提升可读性与效率。
|
7月前
|
机器学习/深度学习 传感器 分布式计算
数据才是真救命的:聊聊如何用大数据提升灾难预警的精准度
数据才是真救命的:聊聊如何用大数据提升灾难预警的精准度
475 14
|
4月前
|
存储 供应链 API
1688店铺详情API使用指南
1688店铺详情API是阿里巴巴开放平台核心接口,支持通过店铺ID获取商家基本信息、资质、等级及主营类目等数据,适用于电商分析、供应链对接等场景。本文详解接口参数、Python调用示例及注意事项,助开发者高效集成与应用。
|
8月前
|
人工智能 安全 测试技术
Apifox对决Apipost:API管理工具的深度较量与未来前瞻
在快节奏的软件开发中,API管理工具的选择直接影响效率与协作。本文对比Apipost与Apifox,从界面设计、核心功能、AI能力、离线支持、团队协作、生态整合及性能表现等维度,深入解析两者差异,帮助团队找到更契合的开发利器。
|
8月前
|
人工智能 API
LLMs.txt:AI时代网站的"智能身份证"
当AI模型因HTML冗余代码浪费50%上下文窗口时,LLMs.txt正成为网站与AI对话的新语言。这个轻量级标准已被Anthropic、Cursor等企业采用,让AI理解内容效率提升3倍。
257 0
|
10月前
|
机器学习/深度学习 搜索推荐 算法
“个性化推荐API:动态匹配用户需求,提高客单价”
个性化推荐API通过分析用户行为数据,实时生成定制化推荐,核心价值在于动态匹配需求、提升转化率与客单价。其逻辑基于协同过滤与深度学习算法,结合库存和营销策略优化推荐结果。技术落地需完成数据埋点、算法配置及AB测试,效果评估则关注CTR、转化率等指标。除电商外,该API还适用于内容平台、旅游预订及B2B采购等领域,助力企业扩展商业边界并实现收入增长。
225 5