一、spring security 的三大配置
- 我们配置 spring security 主要是通过继承
WebSecurityConfigurerAdapter
类来实现,其中主要有 3 个方法configure(AuthenticationManagerBuilder auth)
,此方法用于设置用户的权限认证configure(HttpSecurity http)
,此方法用于配置 http 协议,比如设置session
管理,设置是否启用 CSRF 等configure(WebSecurity web)
,此方法用于配置访问资源的限制,经常用来过滤静态资源的校验
二、启动流程
@EnableWebSecurity
- 使用
@Import
实例化了类WebSecurityConfiguration
,该类主要做了以下两个工作setFilterChainProxySecurityConfigurer
方法将我们自定义的配置信息存放在webSecurity
对象中.
webSecurity 其实就是一个 filter,但是需要通过调用 build 才能返回 filterspringSecurityFilterChain
方法为我们创建一个默认的 filter.
当然如果我们自定义类继承了 WebSecurityConfigurerAdapter 就不会创建默认的
- 使用
三、过滤器初始化
1、在阅读源码之前
在介绍过滤器的初始化之前我们先记住下面这个继承关系,便于后面的理解
- SecurityBuilder 接口只有一个
build()
方法 - AbstractSecurityBuilder 使用 CAS 保证
build()
方法的原子性,然后将真正的build()
操作交给doBuild()
- AbstractConfiguredSecurityBuilder 实现
doBuild()
方法,定义了具体的步骤以及 build 的各个状态 - WebSecurity 实现
performBuild()
来完成最终的真正的 build
这几个类就是设计模式中很典型的
模板模式
2、初始化过程
在之前的启动流程中我们说到 WebSecurityConfiguration
类的 springSecurityFilterChain
方法会为我们创建一个 filter,看一下代码:
@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();
}
在这个方法的最后一行,我们看到了调用了 build()
方法,这个方法就是过滤器初始化的入口了,build()
流程如下:
- 执行父类
AbstractSecurityBuilder
的build()
- 执行父类
AbstractConfiguredSecurityBuilder
的doBuild()
- 执行当前类也就是
WebSecurity
类的performBuild()
现在我们来仔细看一看 WebSecurity
类的 performBuild()
方法具体做了些什么,源码:
protected Filter performBuild() throws Exception {
// question 1:securityFilterChainBuilders是什么时候初始化的?
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");
// question 2:ignoredRequests什么时候初始化的?
int chainSize = ignoredRequests.size() + securityFilterChainBuilders.size();
List<SecurityFilterChain> securityFilterChains = new ArrayList<>(
chainSize);
// 将ignoredRequests和securityFilterChainBuilders中的元素封装一下,然后存放进securityFilterChains中
for (RequestMatcher ignoredRequest : ignoredRequests) {
securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest));
}
for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) {
securityFilterChains.add(securityFilterChainBuilder.build());
}
// 代理securityFilterChains
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;
}
- question 1
AbstractConfiguredSecurityBuilder#doBuild()
->AbstractConfiguredSecurityBuilder#init()
->WebSecurityConfigurerAdapter#init(..)
->WebSecurity#addSecurityFilterChainBuilder(..)
- 最终在 addSecurityFilterChainBuilder 方法中初始化了
securityFilterChainBuilders
,你要是问我doBuild
方法又是哪来的,很简单之前就说过了,是 WebSecurity 的 build()调用的
- question 2
IgnoredRequestConfigurer#mvcMatchers(..)
->WebSecurity#mvcMatchers(..)
- 那系统是在何时使用过
IgnoredRequestConfigurer#mvcMatchers(..)
方法的呢?很简单
我们在使用 spring security 的时候都会继承WebSecurityConfigurerAdapter
类重新一些配置方法,其中有一个配置方法一般会如下使用:@Override public void configure(WebSecurity web) throws Exception { web.ignoring().mvcMatchers("/images/**"); web.ignoring().mvcMatchers("/js/**"); web.ignoring().mvcMatchers("/css/**"); web.ignoring().mvcMatchers("/lib/**"); // web.ignoring()方法只是返回内部类IgnoredRequestConfigurer的实例 // 我们就是在这儿使用了IgnoredRequestConfigurer#mvcMatchers(..) }
- 上面的这两个问题解决了,接着就是各种代理了,然后返回被代理的过滤器链
总结一下本方法的作用:就是找到我们配置或者默认配置的一些过滤器,然后代理封装一下,最后组成一个过滤器链再返回
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于