SpringSecurity 的初始化流程
初始化从 SpringSecurity 的自动化配置类开始
@Configuration(proxyBeanMethods = false)@ConditionalOnClass(DefaultAuthenticationEventPublisher.class)@EnableConfigurationProperties(SecurityProperties.class)@Import({ SpringBootWebSecurityConfiguration.class, WebSecurityEnablerConfiguration.class, SecurityDataConfiguration.class })public class SecurityAutoConfiguration {
@Bean @ConditionalOnMissingBean(AuthenticationEventPublisher.class) public DefaultAuthenticationEventPublisher authenticationEventPublisher(ApplicationEventPublisher publisher) { return new DefaultAuthenticationEventPublisher(publisher); }
}
复制代码
WebSecurityEnablerConfiguration 是重点
@Configuration(proxyBeanMethods = false)@ConditionalOnBean(WebSecurityConfigurerAdapter.class)@ConditionalOnMissingBean(name = BeanIds.SPRING_SECURITY_FILTER_CHAIN)@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)@EnableWebSecuritypublic class WebSecurityEnablerConfiguration {
}
复制代码
@Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)@Target(value = { java.lang.annotation.ElementType.TYPE })@Documented@Import({ WebSecurityConfiguration.class, SpringWebMvcImportSelector.class, OAuth2ImportSelector.class })@EnableGlobalAuthentication@Configurationpublic @interface EnableWebSecurity { boolean debug() default false;}
复制代码
EnableWebSecurity 导入了 WebSecurityConfiguration,用来配置 WebSecurity
EnableGlobalAuthentication 注解导入了配置类 AuthenticationConfiguration
@Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)@Target(value = { java.lang.annotation.ElementType.TYPE })@Documented@Import(AuthenticationConfiguration.class)@Configurationpublic @interface EnableGlobalAuthentication {}
复制代码
重点分析这两个配置
WebSecurityConfiguration
WebSecurityConfiguration 实现了 ImportAware 接口,使用 @Import 注解在 @EnableWebSecurity 上导入 WebSecurityConfiguration 之后,在 WebSecurityConfiguration 的 setImportMetadata 方法方便获取到 @EnableWebSecurity 注解中的属性值。
WebSecurityConfiguration 实现了 BeanClassLoaderAware 方便获取 ClassLoader 对象
重点看 setFilterChainProxySecurityConfigurer 方法:主要是用来构建一个 WebSecurity 对象,并且加载所有的配置类对象。
@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); }
webSecurityConfigurers.sort(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;}
复制代码
手写创建一个 WebSecurity,创建出来之后的对象去对象处理后置器中处理,将 webSecurity 对象注册到 Spring 容器中。
然后根据每个配置类的 @Order 注解对 webSecurityConfigurations 集合中的所有配置类进行排序,因为一个配置类对应一个过滤器链,因为请求到来时需要先和那个过滤器匹配存在优先级问题。
排序后进入 for 循环,检查是否存在优先级相等问题,如果存在直接抛出异常。最后遍历所有配置类,调用 webSecurity.apply 方法加到 WebSecurity 父类中的 configs 集合中。
有了 WebSecurity 对象和配置类就可以构建过滤器 FilterChainProxy 了
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();}
复制代码
先判断 webSecurityConfigurers 集合中是否存在配置类,如果不存在立马创建一个匿名的 WebSecurityConfigurerAdapter,否则直接调用
webSecurity.build()进行构建,对所有的配置类 WebSecurityConfigurerAdapter 实例进行构建,在 WebSecurityConfigurerAdapter 的 init 方法中又完成 HttpSecurity 的构建,HttpSecurity 构建过程中完成局部 AuthenticationManager 对象和每一个具体过滤器的构建。
AuthenticationConfiguration
导入 ObjectPostProcessorConfiguration 配置类,具体实现类是 AutowireBeanFactoryObjectPostProcessor,将一个对象注册到 Spring 容器中。
@Import(ObjectPostProcessorConfiguration.class)public class AuthenticationConfiguration {
复制代码
构建 AuthenticationManager
public AuthenticationManager getAuthenticationManager() throws Exception { if (this.authenticationManagerInitialized) { return this.authenticationManager; } AuthenticationManagerBuilder authBuilder = this.applicationContext.getBean(AuthenticationManagerBuilder.class); if (this.buildingAuthenticationManager.getAndSet(true)) { return new AuthenticationManagerDelegator(authBuilder); }
for (GlobalAuthenticationConfigurerAdapter config : globalAuthConfigurers) { authBuilder.apply(config); }
authenticationManager = authBuilder.build();
if (authenticationManager == null) { authenticationManager = getAuthenticationManagerBean(); }
this.authenticationManagerInitialized = true; return authenticationManager;}
复制代码
AuthenticationConfiguration 作用:
导入 ObjectPostProcessorConfiguration 配置类,具体实现类是 AutowireBeanFactoryObjectPostProcessor,将一个对象注册到 Spring 容器中。
提供全局的 AuthenticationManager
如果重写了 AuthenticationManagerBuilder 的 configure 方法,全局 AuthenticationManager 失效,大部分情况下 我们会重写 AuthenticationManagerBuilder 的 configure 方法。
评论