〇、上篇回顾
- 整个框架的核心就是构建一个名字为
springSecurityFilterChain
的过滤器,它的类型是FilterChainProxy
- 框架的主要参与者是
建造者
和配置器
,其中WebSecurity
和HttpSecurity
都是建造者
WebSecurity
的构建目标是FilterChainProxy
对象,即核心过滤器springSecurityFilterChain
HttpSecurity
的构建目标只是FilterChainProxy
对象中一组SecurityFilterChain
的一个配置器
主要关注init()
、configure()
方法
一、WebSecurityConfiguration
第一篇《Spring Security源码(一):整体框架设计》文章提到,看源码可以从
@EnableWebSecurity
注解开始,点进去发现它引入了类WebSecurityConfiguration
,类中正好有生成核心过滤器的Bean方法springSecurityFilterChain()
,还需要关注类中另一个方法setFilterChainProxySecurityConfigurer()
,一起来看看。
setFilterChainProxySecurityConfigurer()
- 创建
WebSecurity
建造者对象,apply()
初始配置。
@Autowired(required = false)
public void setFilterChainProxySecurityConfigurer(
ObjectPostProcessor<Object> objectPostProcessor,
@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers)
throws Exception {
webSecurity = objectPostProcessor
.postProcess(new WebSecurity(objectPostProcessor));
if (debugEnabled != null) {
webSecurity.debug(debugEnabled);
}
Collections.sort(webSecurityConfigurers, AnnotationAwareOrderComparator.INSTANCE);
Integer previousOrder = null;
Object previousConfig = null;
for (SecurityConfigurer<Filter, WebSecurity> config : webSecurityConfigurers) {
Integer order = AnnotationAwareOrderComparator.lookupOrder(config);
if (previousOrder != null && previousOrder.equals(order)) {
throw new IllegalStateException(
"@Order on WebSecurityConfigurers must be unique. Order of "
+ order + " was already used on " + previousConfig + ", so it cannot be used on "
+ config + " too.");
}
previousOrder = order;
previousConfig = config;
}
for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
webSecurity.apply(webSecurityConfigurer);
}
this.webSecurityConfigurers = webSecurityConfigurers;
}
springSecurityFilterChain()
- 建造者
webSecurity
调用build()
方法,开始构建springSecurityFilterChain
。
@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
public Filter springSecurityFilterChain() throws Exception {
boolean hasConfigurers = webSecurityConfigurers != null
&& !webSecurityConfigurers.isEmpty();
if (!hasConfigurers) {
WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor
.postProcess(new WebSecurityConfigurerAdapter() {
});
webSecurity.apply(adapter);
}
return webSecurity.build();
}
二、FilterChainProxy的创建过程
创建过程
springSecurityFilterChain()
方法中,webSecurity
调用了build()
方法- 方法进去继续调用了
doBuild()
,该方法依次调用各个配置器的init()
、configure()
方法 - 等配置到位后,开始执行
performBuild()
,进行核心过滤器FilterChainProxy
的创建
@Override
protected final O doBuild() throws Exception {
synchronized (configurers) {
buildState = BuildState.INITIALIZING;
beforeInit();
init();
buildState = BuildState.CONFIGURING;
beforeConfigure();
configure();
buildState = BuildState.BUILDING;
O result = performBuild();
buildState = BuildState.BUILT;
return result;
}
}
webSecurity
调用的performBuild()
方法就创建了核心过滤器FilterChainProxy
。
注意创建过程可不是这么简单的由
webSecurity
一次性调用下来,就是说doBuild()
可不只是被webSecurity
对象调用,会被很多创建者重复调用,重复的去走创建流程,比如被一堆的HttpSecurity
调用目的是创建单条过滤器链,可以自己Debug去走一走。
performBuild() 介绍
回顾第二章《Spring Security源码(二):建造者详解》,该方法被
WebSecurity
、HttpSecurity
、AuthenticationManagerBuilder
三个建造者所实现,此篇先不关心后者。
HttpSecurity#performBuild
:创建核心过滤器中单条过滤器链:SecurityFilterChain
@Override
protected DefaultSecurityFilterChain performBuild() throws Exception {
Collections.sort(filters, comparator);
return new DefaultSecurityFilterChain(requestMatcher, filters);
}
WebSecurity#performBuild
:创建核心过滤器:FilterChainProxy
@Override
protected Filter performBuild() throws Exception {
Assert.state(
!securityFilterChainBuilders.isEmpty(),
"At least one SecurityBuilder<? extends SecurityFilterChain> needs to be specified. Typically this done by adding a @Configuration that extends WebSecurityConfigurerAdapter. More advanced users can invoke "
+ WebSecurity.class.getSimpleName()
+ ".addSecurityFilterChainBuilder directly");
int chainSize = ignoredRequests.size() + securityFilterChainBuilders.size();
List<SecurityFilterChain> securityFilterChains = new ArrayList<SecurityFilterChain>(
chainSize);
for (RequestMatcher ignoredRequest : ignoredRequests) {
securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest));
}
for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) {
securityFilterChains.add(securityFilterChainBuilder.build());
}
FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
if (httpFirewall != null) {
filterChainProxy.setFirewall(httpFirewall);
}
filterChainProxy.afterPropertiesSet();
Filter result = filterChainProxy;
if (debugEnabled) {
logger.warn("\n\n"
+ "********************************************************************\n"
+ "********** Security debugging is enabled. *************\n"
+ "********** This may include sensitive information. *************\n"
+ "********** Do not use in a production system! *************\n"
+ "********************************************************************\n\n");
result = new DebugFilter(filterChainProxy);
}
postBuildAction.run();
return result;
}
securityFilterChainBuilders 什么时候插入的?
上面
WebSecurity#performBuild
方法中循环所有的securityFilterChainBuilders
创建单条过滤器链,其中的数据是什么时候插入的?
- 其实在上一章《Spring Security源码(四):配置器详解》讲到了,是在
WebSecurityConfigurerAdapter#init
方法中插入的。
三、Debug
结果分析
-
可以看到核心过滤器
FilterChainProxy
有两个过滤器链,其中一个就是我们自己定义的,有14个过滤器,部分是我们定义的部分是系统默认的,仔细发现两个过滤器链最后一个过滤器都是FilterSecurityInterceptor
。
FilterSecurityInterceptor
- 这个类是在
WebSecurityConfigurerAdapter#init
方法中创建的,创建了一个新线程,最后在WebSecurity#performBuild
中启动该线程postBuildAction.run();
将这个过滤器添加到每个过滤器链的末位,在认证过程中保证该过滤器是最后执行的。 - 这个过滤器是整个链条的最后一环,过了它就可以访问我们后台的资源服务了。
Spring Security的访问控制
功能,它起到了至关重要的作用,后面文章来对它进行详细解析。
四、总结
- 其实找到源码入口,跟踪方法调用链,再去了解下相关的一些建造者、配置器类,也就基本知道核心过滤器是怎么创建的了。
五、系列文章
Spring Security 系列
- 《手把手教你如何使用Spring Security(上):登录授权》
- 《手把手教你如何使用Spring Security(中):接口认证》
- 《手把手教你如何使用Spring Security(下):访问控制》
- 《Spring Security源码(一):整体框架设计》
- 《Spring Security源码(二):建造者详解》
- 《Spring Security源码(三):HttpSecurity详解》
- 《Spring Security源码(四):配置器详解》
- 《Spring Security源码(五):FilterChainProxy是如何创建的?》
- 《Spring Security源码(六):FilterChainProxy是如何运行的?》
- 《Spring Security源码(七):设计模式在框架中的应用》
- 《Spring Security源码(八):登录认证源码流程》
- 《Spring Security源码(九):过滤器链上的过滤器是如何排序的?》
- 《Spring Security源码(十):权限访问控制是如何做到的?》
Spring Security OAuth 系列
Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。
它的内容包括:
- 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
- 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
- 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
- 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
- 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
- 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
- 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
- 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw
目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:
想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询
同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。