SpringSecurity------AuthenticationConfiguration配置类
一、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如果已经被构建则直接返回authenticationManager
if (this.authenticationManagerInitialized) {
return this.authenticationManager;
}
//获取容器中的AuthenticationManagerBuilder实例用于创建AuthenticationManager
AuthenticationManagerBuilder 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);
}
//构建AuthenticationManager
this.authenticationManager = authBuilder.build();
// 如果容器中没有用于构建 AuthenticationManager 的 AuthenticationProvider bean
// 供 authBuilder 使用,也没有为 authBuilder 设置 parent AuthenticationManager 时,
// 则上面产生的 authenticationManager 为 null 。 不过这种情况缺省情况下并不会发生,
// 因为该配置类中 bean InitializeUserDetailsBeanManagerConfigurer 为 authBuilder
// 添加的 InitializeUserDetailsBeanManagerConfigurer 会在这种情况下构造一个
// DaoAuthenticationProvider 对象给 authBuilder 使用。另外,一般情况下,开发人员也会
// 提供自己的 AuthenticationProvider 实现类。
// 通常经过上面的 authBuilder.build(),authenticationManager 对象都会被创建,
// 但是如果 authenticationManager 未被创建,这里尝试使用 getAuthenticationManagerBean()
// 再次设置 authenticationManager
if (this.authenticationManager == null) {
this.authenticationManager = getAuthenticationManagerBean();
}
//将authenticationManagerInitialized 设置为true,说明authenticationManager已经初始化完成
this.authenticationManagerInitialized = true;
//返回构建好的AuthenticationManager
return 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注解的全局配置
@Override
public 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;
}
//具有幂等性且进行了同步处理
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
//如果已经包含创建成功的AuthenticationManager,直接调用AuthenticationManager.authenticate()方法返回一个Authentication
if (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()方法返回一个Authentication
return this.delegate.authenticate(authentication);
}
@Override
public 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;
}
@Override
public InMemoryUserDetailsManagerConfigurer<AuthenticationManagerBuilder> inMemoryAuthentication()
throws Exception {
return super.inMemoryAuthentication().passwordEncoder(this.defaultPasswordEncoder);
}
@Override
public JdbcUserDetailsManagerConfigurer<AuthenticationManagerBuilder> jdbcAuthentication() throws Exception {
return super.jdbcAuthentication().passwordEncoder(this.defaultPasswordEncoder);
}
@Override
public <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;
}
@Override
public String encode(CharSequence rawPassword) {
return getPasswordEncoder().encode(rawPassword);
}
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
return getPasswordEncoder().matches(rawPassword, encodedPassword);
}
@Override
public 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;
}
@Override
public String toString() {
return getPasswordEncoder().toString();
}
}