「这是我参与2022首次更文挑战的第14天,活动详情查看:2022首次更文挑战」
前言
- 前面我们学习了springboot中如何创建DelegatingFilterProxy的过程。哪里我们只是简单介绍springboot方式如何向servlet创建Filter 。 那篇文章主要介绍了如何注册。本文我们就当详细剖析下创建流程
入口
- 在上面我们知道
DelegatingFilterProxyRegistrationBean注册了DelegatingFilterProxy。而真正创建过滤器的地方是在WebSecurityConfiguration中会创建名叫springSecurityFilterChain的Filter
webSecurityConfigures
-
private List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers; -
他就是一个List里面存储了SecurityConfigurer配置。他的加载在
WebSecurityConfiguration#setFilterChainProxySecurityConfigurer方法中。 -
在方法中通过@Value方式注入了我们获取配置的方法。
public List<SecurityConfigurer<Filter, WebSecurity>> getWebSecurityConfigurers() {
List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers = new ArrayList<>();
Map<String, WebSecurityConfigurer> beansOfType = beanFactory
.getBeansOfType(WebSecurityConfigurer.class);
for (Entry<String, WebSecurityConfigurer> entry : beansOfType.entrySet()) {
webSecurityConfigurers.add(entry.getValue());
}
return webSecurityConfigurers;
}
-
beanFactory#getBeansOfType获取的是spring容器中所有WebSecurityConfigurer本身及其子类的bean 。而我们security的配置类正是继承了WebSecirityConfigurerAdapter -
我们通过类图又知道
WebSecurityConfigurerAdapter是WebSecurityConfigurer的实现类。所有在getWebSecurityConfigurers中获取到的信息正式我们security的配置类。 -
而在
setFilterChainProxySecurityConfigurer中除了初始化我们需要的webSecurityConfigurers以外还会初始化我们另外一个参数webSecurity。他的创建时通过objectPostProcessor对普通类完成spring bean的转换的,这个我们之前也提到过。
for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
webSecurity.apply(webSecurityConfigurer);
}
- 最后会将
webSecurityConfigurer赋值给webSecurity。
回到主线
-
上面我们穿插了下说明
webSecurityConfigurers属性的加载实际上就是我们自定义的SecurityConfig。期间同时完成了webSecurity的初始化。下面就开始了我们webSecurity.build操作 -
build操作实际上就是
WebSecurity完成的,只不过有些方法依赖于父类进行调度 -
根据这个时序图我们可以明显看出最终核心在其父类的
init、configure、performBuild三个方法中。
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;
}
}
- 在
AbstractConfiguredSecurityBuilder#doBuild中我们可以看到有四种状态用来标识建造状态。其中before开头的两个方法交由子类实现也就是有WebSecurity,追踪下源码发现WebSecurity并未实现,所以这两个方法我们暂时忽略
INITIALIZING
- 首先进入的就是initializing状态。这个期间我们执行的init(); 源码点进去看看执行逻辑。
private void init() throws Exception {
Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
for (SecurityConfigurer<O, B> configurer : configurers) {
configurer.init((B) this);
}
for (SecurityConfigurer<O, B> configurer : configurersAddedInInitializing) {
configurer.init((B) this);
}
}
-
不难看出首先是遍历
WebSecurityConfigurer#init。由上面我们也知道实际就是我们SecurityConfig配置类。最终的init也是由WebSecurityConfigurerAdapter来完成的。 -
关于这段代码,他的作用就是构建一个
HttpSecurity赋值给WebSecurity并对他添加一个拦截器。 -
在
SecurityConfig中我们配置了相关测试信息。那么在WebSecurityConfigurerAdapter中的getHttp一定是依据我们构建的信息进行构建HttpSecurity对象。 -
进入init之后就能发现两个变量
localConfigureAuthenticationBldr、authenticationBuilder。有趣的是这两个变量是相同的类定义的。为什么要多此一举呢?这里买个坑以后有时间回来补! -
还是在
WebSecurityConfigurerAdapter中设置上下文的地方对这两个变量进行了初始化
@Autowired
public void setApplicationContext(ApplicationContext context) {
this.context = context;
ObjectPostProcessor<Object> objectPostProcessor = context.getBean(ObjectPostProcessor.class);
LazyPasswordEncoder passwordEncoder = new LazyPasswordEncoder(context);
authenticationBuilder = new DefaultPasswordEncoderAuthenticationManagerBuilder(objectPostProcessor, passwordEncoder);
localConfigureAuthenticationBldr = new DefaultPasswordEncoderAuthenticationManagerBuilder(objectPostProcessor, passwordEncoder) {
@Override
public AuthenticationManagerBuilder eraseCredentials(boolean eraseCredentials) {
authenticationBuilder.eraseCredentials(eraseCredentials);
return super.eraseCredentials(eraseCredentials);
}
};
}
- 两者不同之处就是后者会复用前者的
eraseCredentials方法。 - 让我们回到getHttp方法内部。首先是设置
DefaultAuthenticationEventPublisher。然后是AuthenticationManager. 关于AuthenticationManager我们可以理解成认证管理器。在权限功能中认证是至关重要的,所以关于authenticationManager方法我们是必须点进去一探究竟的。
protected AuthenticationManager authenticationManager() throws Exception {
if (!authenticationManagerInitialized) {
configure(localConfigureAuthenticationBldr);
if (disableLocalConfigureAuthenticationBldr) {
authenticationManager = authenticationConfiguration
.getAuthenticationManager();
}
else {
authenticationManager = localConfigureAuthenticationBldr.build();
}
authenticationManagerInitialized = true;
}
return authenticationManager;
}
- 首先根据
authenticationManagerInitialized属性判断是否已经初始化过,如果是第一次初始化那么就会进入下面逻辑否则直接返回已有对象。 - 首当其冲就是执行configure(AuthenticationManagerBuilder)方法,这里对应我们securityConfig中的代码
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("zxhtom").password(new BCryptPasswordEncoder().encode("123456")).roles("admin");
}
- 因为我们重写了configure(AuthenticationManagerBuilder)方法,所以父类
WebSecurityConfigurerAdapter的默认方法就会失效
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
this.disableLocalConfigureAuthenticationBldr = true;
}
- 所以disableLocalConfigureAuthenticationBldr=false。 所以直接执行
localConfigureAuthenticationBldr.build();