SpringSecurity------AuthenticationConfiguration配置类
- 一、AuthenticationConfiguration是怎样被加载的
- 二、AuthenticationConfiguration主要做了什么
- 三、AuthenticationConfiguration的源码分析
-
- 1、属性字段
- 2、核心方法
-
- 内部构建AuthenticationManagerBuilder 的方法authenticationManagerBuilder()
- 对外构建AuthenticationManager方法getAuthenticationManager()
- 3、使用@Bean初始化GlobalAuthenticationConfigurerAdapter的三个实现
- 4、使用@Autowired将一些Bean注入到当前配置类的属性中
- 5、当前类的私有方法、类注解、内部类
-
- 当前配置类的一些私有方法
- 类注解@Import(ObjectPostProcessorConfiguration.class)
- 一些内部类
一、AuthenticationConfiguration是怎样被加载的
通过@EnableWebSecurity引入@EnableGlobalAuthentication注解,源码如下:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import({
WebSecurityConfiguration.class, SpringWebMvcImportSelector.class, OAuth2ImportSelector.class,HttpSecurityConfiguration.class })
@EnableGlobalAuthentication
@Configuration
public @interface EnableWebSecurity {
/*** Controls debugging support for Spring Security. Default is false.* @return if true, enables debug support with Spring Security*/boolean debug() default false;}
在@EnableGlobalAuthentication注解上使用@Import(AuthenticationConfiguration.class)注解引入本类
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(AuthenticationConfiguration.class)
@Configuration
public @interface EnableGlobalAuthentication {
}
二、AuthenticationConfiguration主要做了什么
简单的说,这个类的作用就是用来创建ProviderManager,ProviderManager是一个AuthenticationManager实现,用于管理所有AuthenticationProvider实现的一个管理器。
首先,AuthenticationConfiguration类上的@Import()注解引入一个ObjectPostProcessorConfiguration类,该类会向ApplicationContext中添加一个ObjectPostProcessor的实现类AutowireBeanFactoryObjectPostProcessor,这个类提供spring的一些生命周期支持,被用于创建一些Bean。
然后,我们可以看到当前配置类上有两个关键属性authenticationManager和globalAuthConfigurers,其中authenticationManager用于存储一个AuthenticationManager对象,构建这个对象就是当前配置类的主要功能了;globalAuthConfigurers是存储全局配置的一个列表,三个默认的GlobalAuthenticationConfigurerAdapter实现会被注入到这个属性列表中,他们分别是:EnableGlobalAuthenticationAutowiredConfigurer(当前配置类的一个内部类)、InitializeAuthenticationProviderBeanManagerConfigurer、InitializeUserDetailsBeanManagerConfigurer,这三个类都是在当前配置类中使用@Bean注解引入,然后通过@Autowired注解的setGlobalAuthenticationConfigurers()方法将他们注入到globalAuthConfigurers属性中(源码分析中有具体介绍)。
接着,我们来看看创建AuthenticationManager的getAuthenticationManager()方法,他会从applicationContext中获取一个AuthenticationManagerBuilder,然后将全局配置globalAuthConfigurers中的配置设置到AuthenticationManagerBuilder中,然后使用这个AuthenticationManagerBuilder来创建一个AuthenticationManager对象。(具体的实现细节,下面的源码分析有介绍)。
那么,容器中的AuthenticationManagerBuilder是在那里初始化的呢?我们可以看到,在当前配置类中有一个带有@Bean注解的authenticationManagerBuilder()方法,这个方法创建一个DefaultPasswordEncoderAuthenticationManagerBuilder,他是AuthenticationManagerBuilder的一个实现类,这个类使用密码解码器的延时加载策略LazyPasswordEncoder,如果能从applicationContext中获取到AuthenticationEventPublisher,也会将这个事件发布器设置到AuthenticationManagerBuilder中。
总结,AuthenticationConfiguration会获取到容器中所有的GlobalAuthenticationConfigurerAdapter实现,然后创建一个默认的AuthenticationManagerBuilder(就是DefaultPasswordEncoderAuthenticationManagerBuilder),接着将所有GlobalAuthenticationConfigurerAdapter配置设置到DefaultPasswordEncoderAuthenticationManagerBuilder中,然后提供一个对外方法getAuthenticationManager(),这个方法中会获取到DefaultPasswordEncoderAuthenticationManagerBuilder,然后创建一个AuthenticationManager(就是ProviderManager),这就是AuthenticationConfiguration所做的事情。
三、AuthenticationConfiguration的源码分析
1、属性字段
//标志位,AuthenticationManager是否正处于构建过程中
private AtomicBoolean buildingAuthenticationManager = new AtomicBoolean();
//Application容器
private ApplicationContext applicationContext;
//用于记录所要构建的AuthenticationManager
private AuthenticationManager authenticationManager;
//AuthenticationManager是否已经被构建的标志
private boolean authenticationManagerInitialized;
//全局认证配置适配器列表
private List<GlobalAuthenticationConfigurerAdapter> globalAuthConfigurers = Collections.emptyList();
//对象后处理器
private ObjectPostProcessor<Object> objectPostProcessor;
2、核心方法
内部构建AuthenticationManagerBuilder 的方法authenticationManagerBuilder()
构建一个AuthenticationManagerBuilder,用创建AuthenticationManager实例
@Bean
public AuthenticationManagerBuilder authenticationManagerBuilder(ObjectPostProcessor<Object> objectPostProcessor,ApplicationContext context) {
/*** Lazy密码加密器:该对象创建时容器中可能还不存在真正的密码加密器* 但是用该lazy密码加密器进行加密或者密码匹配时,会从容器中获取类型为PasswordEncoder的密码加密器,* 如果容器中不存在类型为PasswordEncoder的密码加密器,则使用* PasswordEncoderFactories.createDelegatingPasswordEncoder()创建一个PasswordEncoder供随后加密或者密码匹配使用* LazyPasswordEncoder是定义在当前配置类中的一个内部类*/LazyPasswordEncoder defaultPasswordEncoder = new LazyPasswordEncoder(context);/***获取鉴权事件的发布器*/AuthenticationEventPublisher authenticationEventPublisher = getBeanOrNull(context,AuthenticationEventPublisher.class);/*** 生成AuthenticationManagerBuilder实例,使用实现类为DefaultPasswordEncoderAuthenticationManagerBuilder* DefaultPasswordEncoderAuthenticationManagerBuilder是定义在该配置类中的一个内部类,它继承自AuthenticationManagerBuilder* 是SpringSecurity缺省使用的 AuthenticationManagerBuilder实现类*/DefaultPasswordEncoderAuthenticationManagerBuilder result = new DefaultPasswordEncoderAuthenticationManagerBuilder(objectPostProcessor, defaultPasswordEncoder);/***如果有事件发布器,则设置*/if (authenticationEventPublisher != null) {
result.authenticationEventPublisher(authenticationEventPublisher);}return result;
}
对外构建AuthenticationManager方法getAuthenticationManager()
根据配置生成认证管理器 AuthenticationManager,该方法具有幂等性且进行了同步处理 。首次调用会触发真正的构建过程生成认证管理器 AuthenticationManager,再次的调用都会返回首次构建的认证管理器 AuthenticationManager。
public AuthenticationManager getAuthenticationManager() throws Exception {
//authenticationManager如果已经被构建则直接返回authenticationManagerif (this.authenticationManagerInitialized) {
return this.authenticationManager;}//获取容器中的AuthenticationManagerBuilder实例用于创建AuthenticationManagerAuthenticationManagerBuilder authBuilder = this.applicationContext.getBean(AuthenticationManagerBuilder.class);// 如果已经正在使用authBuilder进行构建, 则这里直接返回一个包装了构建器authBuilder的AuthenticationManagerDelegator对象// true表示现在正在构建过程中,false表示现在不在构建过程中if (this.buildingAuthenticationManager.getAndSet(true)) {
return new AuthenticationManagerDelegator(authBuilder);}//将全局配置设置到AuthenticationManagerBuilder中for (GlobalAuthenticationConfigurerAdapter config : this.globalAuthConfigurers) {
authBuilder.apply(config);}//构建AuthenticationManagerthis.authenticationManager = authBuilder.build();// 如果容器中没有用于构建 AuthenticationManager 的 AuthenticationProvider bean// 供 authBuilder 使用,也没有为 authBuilder 设置 parent AuthenticationManager 时,// 则上面产生的 authenticationManager 为 null 。 不过这种情况缺省情况下并不会发生,// 因为该配置类中 bean InitializeUserDetailsBeanManagerConfigurer 为 authBuilder// 添加的 InitializeUserDetailsBeanManagerConfigurer 会在这种情况下构造一个 // DaoAuthenticationProvider 对象给 authBuilder 使用。另外,一般情况下,开发人员也会// 提供自己的 AuthenticationProvider 实现类。 // 通常经过上面的 authBuilder.build(),authenticationManager 对象都会被创建,// 但是如果 authenticationManager 未被创建,这里尝试使用 getAuthenticationManagerBean()// 再次设置 authenticationManagerif (this.authenticationManager == null) {
this.authenticationManager = getAuthenticationManagerBean();}//将authenticationManagerInitialized 设置为true,说明authenticationManager已经初始化完成this.authenticationManagerInitialized = true;//返回构建好的AuthenticationManagerreturn this.authenticationManager;
}
3、使用@Bean初始化GlobalAuthenticationConfigurerAdapter的三个实现
定义一个EnableGlobalAuthenticationAutowiredConfigurer,他会加载使用了注解@EnableGlobalAuthentication的Bean,用于配置全局AuthenticationManagerBuilder:
@Bean
public static GlobalAuthenticationConfigurerAdapter enableGlobalAuthenticationAutowiredConfigurer(ApplicationContext context) {
//EnableGlobalAuthenticationAutowiredConfigurer是GlobalAuthenticationConfigurerAdapter的一个实现,当前配置类的内部类。return new EnableGlobalAuthenticationAutowiredConfigurer(context);
}
定义一个InitializeUserDetailsBeanManagerConfigurer配置类,用于在没有配置单例的UserDetailsService时延时配置全局AuthenticationManagerBuilder:
@Bean
public static InitializeUserDetailsBeanManagerConfigurer initializeUserDetailsBeanManagerConfigurer(ApplicationContext context) {
//InitializeUserDetailsBeanManagerConfigurer是GlobalAuthenticationConfigurerAdapter的一个实现return new InitializeUserDetailsBeanManagerConfigurer(context);
}
定义一个InitializeAuthenticationProviderBeanManagerConfigurer配置类,用于在没有配置单例的UserDetailsService时延时加载全局AuthenticationProvider:
@Bean
public static InitializeAuthenticationProviderBeanManagerConfigurer initializeAuthenticationProviderBeanManagerConfigurer(ApplicationContext context) {
//InitializeAuthenticationProviderBeanManagerConfigurer是GlobalAuthenticationConfigurerAdapter的一个实现return new InitializeAuthenticationProviderBeanManagerConfigurer(context);
}
4、使用@Autowired将一些Bean注入到当前配置类的属性中
注入GlobalAuthenticationConfigurerAdapter配置适配器,用于配置全局的AuthenticationManagerBuilder。三个默认的配置类会被注入到这里,他们分别是(这三个类都是在当前配置类中通过@Bean注解引入的):
- EnableGlobalAuthenticationAutowiredConfigurer(当前配置类的一个内部类)
- InitializeAuthenticationProviderBeanManagerConfigurer
- InitializeUserDetailsBeanManagerConfigurer
@Autowired(required = false)
public void setGlobalAuthenticationConfigurers(List<GlobalAuthenticationConfigurerAdapter> configurers) {
configurers.sort(AnnotationAwareOrderComparator.INSTANCE);this.globalAuthConfigurers = configurers;
}
注入Application容器
@Autowired
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
当前配置类上的@Import(ObjectPostProcessorConfiguration.class)引入的ObjectPostProcessorConfiguration会向容器中输出一个AutowireBeanFactoryObjectPostProcessor(ObjectPostProcessor的一个实现类)
@Autowired
public void setObjectPostProcessor(ObjectPostProcessor<Object> objectPostProcessor) {
this.objectPostProcessor = objectPostProcessor;
}
5、当前类的私有方法、类注解、内部类
当前配置类的一些私有方法
private AuthenticationManager getAuthenticationManagerBean() {
return lazyBean(AuthenticationManager.class);
}@SuppressWarnings("unchecked")
private <T> T lazyBean(Class<T> interfaceName) {
LazyInitTargetSource lazyTargetSource = new LazyInitTargetSource();String[] beanNamesForType = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.applicationContext,interfaceName);if (beanNamesForType.length == 0) {
return null;}String beanName = getBeanName(interfaceName, beanNamesForType);lazyTargetSource.setTargetBeanName(beanName);lazyTargetSource.setBeanFactory(this.applicationContext);ProxyFactoryBean proxyFactory = new ProxyFactoryBean();proxyFactory = this.objectPostProcessor.postProcess(proxyFactory);proxyFactory.setTargetSource(lazyTargetSource);return (T) proxyFactory.getObject();
}
private <T> String getBeanName(Class<T> interfaceName, String[] beanNamesForType) {
if (beanNamesForType.length == 1) {
return beanNamesForType[0];}List<String> primaryBeanNames = getPrimaryBeanNames(beanNamesForType);Assert.isTrue(primaryBeanNames.size() != 0, () -> "Found " + beanNamesForType.length + " beans for type "+ interfaceName + ", but none marked as primary");Assert.isTrue(primaryBeanNames.size() == 1,() -> "Found " + primaryBeanNames.size() + " beans for type " + interfaceName + " marked as primary");return primaryBeanNames.get(0);
}
private List<String> getPrimaryBeanNames(String[] beanNamesForType) {
List<String> list = new ArrayList<>();if (!(this.applicationContext instanceof ConfigurableApplicationContext)) {
return Collections.emptyList();}for (String beanName : beanNamesForType) {
if (((ConfigurableApplicationContext) this.applicationContext).getBeanFactory().getBeanDefinition(beanName).isPrimary()) {
list.add(beanName);}}return list;
}
private static <T> T getBeanOrNull(ApplicationContext applicationContext, Class<T> type) {
try {
return applicationContext.getBean(type);} catch (NoSuchBeanDefinitionException notFound) {
return null;}
}
类注解@Import(ObjectPostProcessorConfiguration.class)
这个注解主要是引入ObjectPostProcessorConfiguration 类,该类向容器中注入一个AutowireBeanFactoryObjectPostProcessor实例
@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ObjectPostProcessorConfiguration {
@Bean@Role(BeanDefinition.ROLE_INFRASTRUCTURE)public ObjectPostProcessor<Object> objectPostProcessor(AutowireCapableBeanFactory beanFactory) {
return new AutowireBeanFactoryObjectPostProcessor(beanFactory);}
}
一些内部类
一个默认的GlobalAuthenticationConfigurerAdapter配置适配器实现,会在当前配置类中创建并注入到当前配置类的globalAuthConfigurers 属性中,主要作用是用来加载带有@EnableGlobalAuthentication注解的Bean, 如果是调试模式,还会输出一条日志 : Eagerly initializing XXX
private static class EnableGlobalAuthenticationAutowiredConfigurer extends GlobalAuthenticationConfigurerAdapter {
private final ApplicationContext context;private static final Log logger = LogFactory.getLog(EnableGlobalAuthenticationAutowiredConfigurer.class);EnableGlobalAuthenticationAutowiredConfigurer(ApplicationContext context) {
this.context = context;}//获取所有带有@EnableGlobalAuthentication注解的全局配置@Overridepublic void init(AuthenticationManagerBuilder auth) {
Map<String, Object> beansWithAnnotation = this.context.getBeansWithAnnotation(EnableGlobalAuthentication.class);if (logger.isTraceEnabled()) {
logger.trace(LogMessage.format("Eagerly initializing %s", beansWithAnnotation));}}
}
AuthenticationManagerDelegator 是AuthenticationManager的一个包装类或是委托类,主要是为了防止在初始化AuthenticationManager时发生无限递归:
- 当这个内部类被构建时,会注入一个AuthenticationManagerBuilder实例。
- authenticate()方法具有幂等性且进行了同步处理
- 当这个类的authenticate()方法被第一次调用时会使用AuthenticationManagerBuilder创建一个AuthenticationManager保存到这个类的delegate属性中,同时将delegateBuilder置空,然后将实际鉴权处理交给AuthenticationManager。
- 后续再调用authenticate()方法就只是使用已经创建好的AuthenticationManager实例
static final class AuthenticationManagerDelegator implements AuthenticationManager {
private AuthenticationManagerBuilder delegateBuilder;private AuthenticationManager delegate;private final Object delegateMonitor = new Object();//初始化一个AuthenticationManagerBuilder实例AuthenticationManagerDelegator(AuthenticationManagerBuilder delegateBuilder) {
Assert.notNull(delegateBuilder, "delegateBuilder cannot be null");this.delegateBuilder = delegateBuilder;}//具有幂等性且进行了同步处理@Overridepublic Authentication authenticate(Authentication authentication) throws AuthenticationException {
//如果已经包含创建成功的AuthenticationManager,直接调用AuthenticationManager.authenticate()方法返回一个Authenticationif (this.delegate != null) {
return this.delegate.authenticate(authentication);}//如果没有包含创建成功的AuthenticationManager,进入同步方法synchronized (this.delegateMonitor) {
if (this.delegate == null) {
//使用AuthenticationManagerBuilder构建一个AuthenticationManager,//将值设置到AuthenticationManagerDelegator的delegate属性this.delegate = this.delegateBuilder.getObject();this.delegateBuilder = null;}}//调用AuthenticationManager.authenticate()方法返回一个Authenticationreturn this.delegate.authenticate(authentication);}@Overridepublic String toString() {
return "AuthenticationManagerDelegator [delegate=" + this.delegate + "]";}
}
static class DefaultPasswordEncoderAuthenticationManagerBuilder extends AuthenticationManagerBuilder {
private PasswordEncoder defaultPasswordEncoder;/*** Creates a new instance* * @param objectPostProcessor the {@link ObjectPostProcessor} instance to use.*/DefaultPasswordEncoderAuthenticationManagerBuilder(ObjectPostProcessor<Object> objectPostProcessor,PasswordEncoder defaultPasswordEncoder) {
super(objectPostProcessor);this.defaultPasswordEncoder = defaultPasswordEncoder;}@Overridepublic InMemoryUserDetailsManagerConfigurer<AuthenticationManagerBuilder> inMemoryAuthentication()throws Exception {
return super.inMemoryAuthentication().passwordEncoder(this.defaultPasswordEncoder);}@Overridepublic JdbcUserDetailsManagerConfigurer<AuthenticationManagerBuilder> jdbcAuthentication() throws Exception {
return super.jdbcAuthentication().passwordEncoder(this.defaultPasswordEncoder);}@Overridepublic <T extends UserDetailsService> DaoAuthenticationConfigurer<AuthenticationManagerBuilder, T> userDetailsService(T userDetailsService) throws Exception {
return super.userDetailsService(userDetailsService).passwordEncoder(this.defaultPasswordEncoder);}
}
static class LazyPasswordEncoder implements PasswordEncoder {
private ApplicationContext applicationContext;private PasswordEncoder passwordEncoder;LazyPasswordEncoder(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;}@Overridepublic String encode(CharSequence rawPassword) {
return getPasswordEncoder().encode(rawPassword);}@Overridepublic boolean matches(CharSequence rawPassword, String encodedPassword) {
return getPasswordEncoder().matches(rawPassword, encodedPassword);}@Overridepublic boolean upgradeEncoding(String encodedPassword) {
return getPasswordEncoder().upgradeEncoding(encodedPassword);}private PasswordEncoder getPasswordEncoder() {
if (this.passwordEncoder != null) {
return this.passwordEncoder;}PasswordEncoder passwordEncoder = getBeanOrNull(this.applicationContext, PasswordEncoder.class);if (passwordEncoder == null) {
passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();}this.passwordEncoder = passwordEncoder;return passwordEncoder;}@Overridepublic String toString() {
return getPasswordEncoder().toString();}
}