WebSecurityConfigurerAdapter
是Spring Security Config
内置提供的一个WebSecurityConfigurer
抽象实现类。WebSecurityConfigurerAdapter
存在的目的是提供一个方便开发人员配置WebSecurity
的基类。它提供了一组全方位配置WebSecurity
的缺省方法实现。开发人员只要继承WebSecurityConfigurerAdapter
提供自己的实现类,哪怕不覆盖WebSecurityConfigurerAdapter
的任何一个方法,都得到了一个配置WebSecurity
的安全配置器WebSecurityConfigurer
实例。但通常情况下,开发人员都有自己特定的安全配置和要求,这时候就可以在自己提供的WebSecurityConfigurerAdapter
子实现类中提供自己的方法覆盖WebSecurityConfigurerAdapter
相应的方法从而对WebSecurity
实施定制。
WebSecurityConfigurerAdapter
为开发人员提供了如下功能 :
WebSecurity
的WebSecurityConfigurerAdapter
对象,可以指定使用或者不使用缺省配置,默认构造函数使用缺省配置;void configure(AuthenticationManagerBuilder auth)
,允许开发人员配置目标WebSecurity
所使用的AuthenticationManager
的双亲AuthenticationManager
;该方法缺省实现的效果是该双亲AuthenticationManager
来自AuthenticationConfiguration
定义的AuthenticationManager
(其实是来自IoC容器的类型为AuthenticationManager
的一个bean
);
通过覆盖实现该方法,开发人员可以定制认证机制,比如设置成基于内存的认证,基于数据库的认证,基于
LDAP
的认证,甚至这些认证机制的一个组合,设置AuthenticationManager
的双亲关系,所使用的PasswordEncoder
等等;
void configure(WebSecurity web)
,允许开发人员覆盖实现配置WebSecurity
,比如设置哪些URL
要忽略安全等等;
通过覆盖实现该方法,开发人员可以定制
WebSecurity
,主要是除了HttpSecurity
之外的安全控制,比如忽略某些静态公开资源或者动态公开资源的安全 ,设置需要使用的防火墙实例,设置权限评估器,安全表达式处理器等;
WebSecurityConfigurerAdapter
自身是一个WebSecurityConfigurer
,它在自己的初始化方法init()
中创建了HttpSecurity http
安全构建器对象,并在缺省情况下(disableDefaults
为false
)应用了如下HttpSecurity
初始配置: http
.csrf().and() // 应用 CsrfConfigurer
.addFilter(new WebAsyncManagerIntegrationFilter()) // 添加过滤器 WebAsyncManagerIntegrationFilter
.exceptionHandling().and() // 应用 ExceptionHandlingConfigurer
.headers().and() // 应用 HeadersConfigurer
.sessionManagement().and() // 应用 SessionManagementConfigurer
.securityContext().and() // 应用 SecurityContextConfigurer
.requestCache().and() // 应用 RequestCacheConfigurer
.anonymous().and() // 应用 AnonymousConfigurer
.servletApi().and() // 应用 ServletApiConfigurer
.apply(new DefaultLoginPageConfigurer<>()).and() // 应用 DefaultLoginPageConfigurer
.logout(); // 应用 LogoutConfigurer
// 使用 SpringFactoriesLoader 加载 classpath 上所有jar包中各自的 META-INF/spring.factories 属性文件
// 中指定的 AbstractHttpConfigurer,应用到 http
ClassLoader classLoader = this.context.getClassLoader();
List<AbstractHttpConfigurer> defaultHttpConfigurers =
SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, classLoader);
for (AbstractHttpConfigurer configurer : defaultHttpConfigurers) {
http.apply(configurer);
}
void configure(HttpSecurity http)
,允许开发人员配置目标HttpSecurity
;这里该方法缺省的实现对HttpSecurity
的安全配置如下:
Http Basic
认证机制;通过覆盖实现该方法,开发人员可以定制
HttpSecurity
;
http
.authorizeRequests().anyRequest().authenticated().and()
// 上面行应用一个 ExpressionUrlAuthorizationConfigurer,要求所有URL必须登录认证后才能访问
.formLogin().and() // 应用 FormLoginConfigurer
.httpBasic(); // 应用 HttpBasicConfigurer
package org.springframework.boot.autoconfigure.security.servlet;
// 省略 imports
@Configuration
@ConditionalOnClass(WebSecurityConfigurerAdapter.class)
@ConditionalOnMissingBean(WebSecurityConfigurerAdapter.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
public class SpringBootWebSecurityConfiguration {
@Configuration
@Order(SecurityProperties.BASIC_AUTH_ORDER)
static class DefaultConfigurerAdapter extends WebSecurityConfigurerAdapter {
}
}
开发人员通常使用类似这种方式配置
WebSecurity
。
// 配置类 WebSecurityConfiguration 代码片段
@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();
}
源代码版本 : 5.1.4.RELEASE
package org.springframework.security.config.annotation.web.configuration;
// 省略 imports
@Order(100)
public abstract class WebSecurityConfigurerAdapter implements
WebSecurityConfigurer<WebSecurity> {
private final Log logger = LogFactory.getLog(WebSecurityConfigurerAdapter.class);
private ApplicationContext context;
private ContentNegotiationStrategy contentNegotiationStrategy = new HeaderContentNegotiationStrategy();
// 在每个安全对象创建之后需要执行后置动作的 后置动作处理器,这里的缺省值
// 其实只是抛出异常声明IoC容器中必须存在一个ObjectPostProcessor bean:
// 参考 @EnableWebSecurity => @EnableGlobalAuthentication
// => AuthenticationConfiguration => ObjectPostProcessorConfiguration
private ObjectPostProcessor<Object> objectPostProcessor = new ObjectPostProcessor<Object>() {
public <T> T postProcess(T object) {
throw new IllegalStateException(
ObjectPostProcessor.class.getName()
+ " is a required bean. Ensure you have used @EnableWebSecurity and @Configuration");
}
};
// 配置 WebSecurity 需要使用到的认证配置,可以认为是全局认证配置,会通过 set 方法被自动注入,
// 该属性会用于从IoC容器获取目标 WebSecurity/HttpSecurity 所要直接使用的 AuthenticationManager 的双亲
// AuthenticationManager 。 该方式可能用得上,也可能用不上,要看开发人员是配置使用
// localConfigureAuthenticationBldr 还是使用该属性用于构建目标 WebSecurity/HttpSecurity 所要直接使用的
// AuthenticationManager 的双亲 AuthenticationManager。
private AuthenticationConfiguration authenticationConfiguration;
// AuthenticationManager 构建器,缺省使用 : DefaultPasswordEncoderAuthenticationManagerBuilder
// 所有构建的 AuthenticationManager 会是目标 WebSecurity/HttpSecurity 所要直接使用的 AuthenticationManager
private AuthenticationManagerBuilder authenticationBuilder;
// AuthenticationManager 构建器,缺省使用 : DefaultPasswordEncoderAuthenticationManagerBuilder
// 所要构建的 AuthenticationManagerBuilder 会是目标 WebSecurity/HttpSecurity 所要直接使用的
// AuthenticationManager 的双亲 AuthenticationManager。 不过缺省情况下,也就是开发人员不在子类
// 中覆盖实现 void configure(AuthenticationManagerBuilder auth) 的情况下, 该 localConfigureAuthenticationBldr
// 不会被用于构建目标 WebSecurity/HttpSecurity 所要直接使用的 AuthenticationManager 的双亲
// AuthenticationManager, 这种情况下的双亲 AuthenticationManager 会来自 authenticationConfiguration
private AuthenticationManagerBuilder localConfigureAuthenticationBldr;
// 是否禁用 localConfigureAuthenticationBldr, 缺省情况下,也就是开发人员不在子类中覆盖实现
// void configure(AuthenticationManagerBuilder auth) 的情况下, 当前 WebSecurityConfigurerAdapter
// 缺省提供的 void configure(AuthenticationManagerBuilder auth) 方法实现会将该标志设置为 true,
// 也就是不使用 localConfigureAuthenticationBldr 构建目标 WebSecurity/HttpSecurity 所要直接使用的
// AuthenticationManager 的双亲 AuthenticationManager, 而是使用 authenticationConfiguration
// 提供的 AuthenticationManager 作为 双亲 AuthenticationManager。
private boolean disableLocalConfigureAuthenticationBldr;
// 标志属性 : 目标 WebSecurity/HttpSecurity 所要直接使用的AuthenticationManager的双亲 authenticationManager
// 是否已经初始化
private boolean authenticationManagerInitialized;
// 目标 WebSecurity/HttpSecurity 所要直接使用的AuthenticationManager的双亲 authenticationManager
private AuthenticationManager authenticationManager;
// 根据传入的 Authentication 的类型判断一个 Authentication 是否可被信任,
// 缺省使用实现机制 AuthenticationTrustResolverImpl
// 可被设置
private AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
// HTTP 安全构建器,用于配置匹配特定URL模式的控制器方法的安全,构建产物是 DefaultSecurityFilterChain
private HttpSecurity http;
// 是否禁用缺省配置,缺省为 false,可以通过当前类构造函数设置为true
private boolean disableDefaults;
/**
* Creates an instance with the default configuration enabled.
* 缺省构造函数, 缺省配置机制启用 : disableDefaults == false
*/
protected WebSecurityConfigurerAdapter() {
this(false);
}
/**
* Creates an instance which allows specifying if the default configuration should be
* enabled. Disabling the default configuration should be considered more advanced
* usage as it requires more understanding of how the framework is implemented.
*
* @param disableDefaults true if the default configuration should be disabled, else
* false
*/
protected WebSecurityConfigurerAdapter(boolean disableDefaults) {
this.disableDefaults = disableDefaults;
}
/**
* 开发人员可以覆盖该方法用于配置指定的 AuthenticationManagerBuilder auth,
* 如果开发人员这么做了,那么这里所被配置的 auth , 其实就是当前配置器的属性
* localConfigureAuthenticationBldr 会被用于构建 WebSecurity/HttpSecurity
* 所要使用的 AuthenticationManager 的双亲 AuthenticationManager。
* 如果开发人员不覆盖实现此方法,此缺省实现其实只是设置一个禁用标志,禁用
* localConfigureAuthenticationBldr, 此时 WebSecurity/HttpSecurity 所要使
* 用的 AuthenticationManager 的双亲 AuthenticationManager 将会来自
* authenticationConfiguration.getAuthenticationManager()
* @param auth the AuthenticationManagerBuilder to use
* @throws Exception
*/
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
this.disableLocalConfigureAuthenticationBldr = true;
}
/**
* Creates the HttpSecurity or returns the current instance
* 创建或者返回已经创建的 HttpSecurity 实例
* 当前 WebSecurityConfigurer 只会创建一个 HttpSecurity 实例 ,
* 如果已经创建,该方法会返回已经创建了的 HttpSecurity 实例 ,而不会再创建一个
* @return the HttpSecurity
* @throws Exception
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
protected final HttpSecurity getHttp() throws Exception {
if (http != null) {
// HttpSecurity 实例已经存在,直接返回使用
return http;
}
DefaultAuthenticationEventPublisher eventPublisher = objectPostProcessor
.postProcess(new DefaultAuthenticationEventPublisher());
localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher);
// 获取 WebSecurity/HttpSecurity 所要直接使用的 AuthenticationManager
// 的双亲 AuthenticationManager
AuthenticationManager authenticationManager = authenticationManager();
// authenticationBuilder 所要构建的目标 AuthenticationManager 才是
// 当前配置器所配置的 WebSecurity/HttpSecurity 所要直接使用的 AuthenticationManager
authenticationBuilder.parentAuthenticationManager(authenticationManager);
authenticationBuilder.authenticationEventPublisher(eventPublisher);
// 创建共享对象
Map<Class<? extends Object>, Object> sharedObjects = createSharedObjects();
// 创建 HttpSecurity 实例
http = new HttpSecurity(objectPostProcessor, authenticationBuilder, sharedObjects);
if (!disableDefaults) {
// HttpSecurity http 的缺省配置逻辑 ====>
http
.csrf().and()
.addFilter(new WebAsyncManagerIntegrationFilter())
.exceptionHandling().and()
.headers().and()
.sessionManagement().and()
.securityContext().and()
.requestCache().and()
.anonymous().and()
.servletApi().and()
.apply(new DefaultLoginPageConfigurer<>()).and()
.logout();
ClassLoader classLoader = this.context.getClassLoader();
// 使用 SpringFactoriesLoader 加载 classpath 上所有jar包中各自的 META-INF/spring.factories 属性文件
// 中指定的 AbstractHttpConfigurer,应用到 http
List<AbstractHttpConfigurer> defaultHttpConfigurers =
SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, classLoader);
for (AbstractHttpConfigurer configurer : defaultHttpConfigurers) {
http.apply(configurer);
}
// HttpSecurity http 的缺省配置逻辑 <====
}
// 使用方法 protected void configure(HttpSecurity http) 配置 HttpSecurity http ,
// 这里如果开发人员重写了该方法,则这里这些开发人员配置逻辑会被应用于配置 HttpSecurity http ,
configure(http);
return http;
}
/**
* Override this method to expose the AuthenticationManager from
* #configure(AuthenticationManagerBuilder) to be exposed as a Bean. For
* example:
*
*
* @Bean(name name="myAuthenticationManager")
* @Override
* public AuthenticationManager authenticationManagerBean() throws Exception {
* return super.authenticationManagerBean();
* }
*
*
* @return the AuthenticationManager
* @throws Exception
*/
public AuthenticationManager authenticationManagerBean() throws Exception {
return new AuthenticationManagerDelegator(authenticationBuilder, context);
}
/**
* Gets the AuthenticationManager to use. The default strategy is if
* #configure(AuthenticationManagerBuilder) method is overridden to use the
* AuthenticationManagerBuilder that was passed in. Otherwise, autowire the
* AuthenticationManager by type.
*
* 获取构建 WebSecurity/HttpSecurity所要使用的 AuthenticationManager 的
* 双亲 AuthenticationManager,这里的策略是 :
* 1. 如果开发人员覆盖实现了 #configure(AuthenticationManagerBuilder) ,
* 则会使用开发人员覆盖实现了的 AuthenticationManagerBuilder , 其实也就是
* 当前配置器的 localConfigureAuthenticationBldr 构建一个 AuthenticationManager
* 并返回和使用;
* 2. 如果开发人员没有覆盖实现 #configure(AuthenticationManagerBuilder) ,
* 则会使用 authenticationConfiguration#getAuthenticationManager() 提供的
* AuthenticationManager, 这是从IoC容器中根据类型查找得到的一个 AuthenticationManager
* @return the AuthenticationManager to use
* @throws Exception
*/
protected AuthenticationManager authenticationManager() throws Exception {
if (!authenticationManagerInitialized) {
// authenticationManager 尚未初始化的情况,在这里进行初始化
// 调用 configure(AuthenticationManagerBuilder auth) 用于配置 localConfigureAuthenticationBldr,
// 该方法有可能被开发人员覆盖实现
configure(localConfigureAuthenticationBldr);
if (disableLocalConfigureAuthenticationBldr) {
// 如果开发人员没有覆盖实现 configure(AuthenticationManagerBuilder auth)
// 方法, 则该方法的缺省实现会设置 disableLocalConfigureAuthenticationBldr=true,
// 这种情况下会使用 authenticationConfiguration 获取IoC容器中配置的 AuthenticationManager
// 作为目标WebSecurity/HttpSecurity 所要直接使用的 AuthenticationManager 的双亲
authenticationManager = authenticationConfiguration
.getAuthenticationManager();
}
else {
// 如果开发人员覆盖实现了 configure(AuthenticationManagerBuilder auth)
// 方法,则 localConfigureAuthenticationBldr 会被用于构建一个 AuthenticationManager,
// 该 AuthenticationManager 会充当目标WebSecurity/HttpSecurity 所要直接使用的
// AuthenticationManager 的双亲
authenticationManager = localConfigureAuthenticationBldr.build();
}
// authenticationManager 初始化完成的情况,设置相应标志
authenticationManagerInitialized = true;
}
return authenticationManager;
}
/**
* Override this method to expose a UserDetailsService created from
* #configure(AuthenticationManagerBuilder) as a bean. In general only the
* following override should be done of this method:
*
*
* @Bean(name = "myUserDetailsService")
* // any or no name specified is allowed
* @Override
* public UserDetailsService userDetailsServiceBean() throws Exception {
* return super.userDetailsServiceBean();
* }
*
*
* To change the instance returned, developers should change
* #userDetailsService() instead
* @return the UserDetailsService
* @throws Exception
* @see #userDetailsService()
*/
public UserDetailsService userDetailsServiceBean() throws Exception {
AuthenticationManagerBuilder globalAuthBuilder = context
.getBean(AuthenticationManagerBuilder.class);
return new UserDetailsServiceDelegator(Arrays.asList(
localConfigureAuthenticationBldr, globalAuthBuilder));
}
/**
* Allows modifying and accessing the UserDetailsService from
* #userDetailsServiceBean() without interacting with the
* ApplicationContext. Developers should override this method when changing
* the instance of #userDetailsServiceBean().
*
* @return the UserDetailsService to use
*/
protected UserDetailsService userDetailsService() {
AuthenticationManagerBuilder globalAuthBuilder = context
.getBean(AuthenticationManagerBuilder.class);
return new UserDetailsServiceDelegator(Arrays.asList(
localConfigureAuthenticationBldr, globalAuthBuilder));
}
// SecurityConfigurer 接口约定的初始化方法
public void init(final WebSecurity web) throws Exception {
// 创建 HttpSecurity http,这是一个 SecurityBuilder
final HttpSecurity http = getHttp();
web.addSecurityFilterChainBuilder(http).postBuildAction(new Runnable() {
public void run() {
FilterSecurityInterceptor securityInterceptor = http
.getSharedObject(FilterSecurityInterceptor.class);
web.securityInterceptor(securityInterceptor);
}
});
}
/**
* Override this method to configure WebSecurity. For example, if you wish to
* ignore certain requests.
* 实现子类需要覆盖实现此方法来自定义配置 WebSecurity
*/
public void configure(WebSecurity web) throws Exception {
}
/**
* Override this method to configure the HttpSecurity. Typically subclasses
* should not invoke this method by calling super as it may override their
* configuration. The default configuration is:
*
*
* http.authorizeRequests().anyRequest().authenticated().and().formLogin().and().httpBasic();
*
*
* 实现子类需要覆盖实现此方法来自定义配置 HttpSecurity,
* 这里的实现是一个缺省实现
* @param http the HttpSecurity to modify
* @throws Exception if an error occurs
*/
protected void configure(HttpSecurity http) throws Exception {
logger.debug(
"Using default configure(HttpSecurity). If subclassed this will potentially override "
+ "subclass configure(HttpSecurity).");
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin().and()
.httpBasic();
}
/**
* Gets the ApplicationContext
* @return the context
*/
protected final ApplicationContext getApplicationContext() {
return this.context;
}
@Autowired
public void setApplicationContext(ApplicationContext context) {
this.context = context;
ObjectPostProcessor<Object> objectPostProcessor = context.getBean(ObjectPostProcessor.class);
// 密码加密器,口令加密器,使用当前 WebSecurityConfigurerAdapter 的内部嵌套类 LazyPasswordEncoder
LazyPasswordEncoder passwordEncoder = new LazyPasswordEncoder(context);
// 目标 WebSecurity/HttpSecurity 所要直接使用的 AuthenticationManager 的构建器
authenticationBuilder = new DefaultPasswordEncoderAuthenticationManagerBuilder(objectPostProcessor,
passwordEncoder);
// 目标 WebSecurity/HttpSecurity 所要直接使用的 AuthenticationManager 的双亲 AuthenticationManager
// 的构建器, 可能被用的上,也可能用不上,要看开发人员是否决定使用这个 localConfigureAuthenticationBldr
localConfigureAuthenticationBldr = new DefaultPasswordEncoderAuthenticationManagerBuilder(
objectPostProcessor, passwordEncoder) {
@Override
public AuthenticationManagerBuilder eraseCredentials(boolean eraseCredentials) {
authenticationBuilder.eraseCredentials(eraseCredentials);
return super.eraseCredentials(eraseCredentials);
}
};
}
// 依赖注入 AuthenticationTrustResolver , 如果容器中有 AuthenticationTrustResolver bean
// 则使用,否则则使用缺省值 : AuthenticationTrustResolverImpl
@Autowired(required = false)
public void setTrustResolver(AuthenticationTrustResolver trustResolver) {
this.trustResolver = trustResolver;
}
// 依赖注入 ContentNegotiationStrategy , 如果容器中有 ContentNegotiationStrategy bean
// 则使用,否则则使用缺省值 : HeaderContentNegotiationStrategy
@Autowired(required = false)
public void setContentNegotationStrategy(
ContentNegotiationStrategy contentNegotiationStrategy) {
this.contentNegotiationStrategy = contentNegotiationStrategy;
}
@Autowired
public void setObjectPostProcessor(ObjectPostProcessor<Object> objectPostProcessor) {
this.objectPostProcessor = objectPostProcessor;
}
@Autowired
public void setAuthenticationConfiguration(
AuthenticationConfiguration authenticationConfiguration) {
this.authenticationConfiguration = authenticationConfiguration;
}
/**
* Creates the shared objects
*
* @return the shared Objects
*/
private Map<Class<? extends Object>, Object> createSharedObjects() {
Map<Class<? extends Object>, Object> sharedObjects = new HashMap<Class<? extends Object>, Object>();
sharedObjects.putAll(localConfigureAuthenticationBldr.getSharedObjects());
sharedObjects.put(UserDetailsService.class, userDetailsService());
sharedObjects.put(ApplicationContext.class, context);
sharedObjects.put(ContentNegotiationStrategy.class, contentNegotiationStrategy);
sharedObjects.put(AuthenticationTrustResolver.class, trustResolver);
return sharedObjects;
}
/**
* Delays the use of the UserDetailsService from the
* AuthenticationManagerBuilder to ensure that it has been fully configured.
*
* @author Rob Winch
* @since 3.2
*/
static final class UserDetailsServiceDelegator implements UserDetailsService {
private List<AuthenticationManagerBuilder> delegateBuilders;
private UserDetailsService delegate;
private final Object delegateMonitor = new Object();
UserDetailsServiceDelegator(List<AuthenticationManagerBuilder> delegateBuilders) {
if (delegateBuilders.contains(null)) {
throw new IllegalArgumentException(
"delegateBuilders cannot contain null values. Got "
+ delegateBuilders);
}
this.delegateBuilders = delegateBuilders;
}
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
if (delegate != null) {
return delegate.loadUserByUsername(username);
}
synchronized (delegateMonitor) {
if (delegate == null) {
for (AuthenticationManagerBuilder delegateBuilder : delegateBuilders) {
delegate = delegateBuilder.getDefaultUserDetailsService();
if (delegate != null) {
break;
}
}
if (delegate == null) {
throw new IllegalStateException("UserDetailsService is required.");
}
this.delegateBuilders = null;
}
}
return delegate.loadUserByUsername(username);
}
}
/**
* Delays the use of the AuthenticationManager build from the
* AuthenticationManagerBuilder to ensure that it has been fully configured.
* 内部嵌套类,该类的目的是包装一个 AuthenticationManager , 该被包装的
* AuthenticationManager 会由该 AuthenticationManagerDelegator 的构造函数
* 参数对象 delegateBuilder 在目标 AuthenticationManager 首次被使用时构建。
* 这么做的目的是确保 AuthenticationManager 被调用时,它已经被完全配置。
*
* @author Rob Winch
* @since 3.2
*/
static final class AuthenticationManagerDelegator implements AuthenticationManager {
private AuthenticationManagerBuilder delegateBuilder;
private AuthenticationManager delegate;
private final Object delegateMonitor = new Object();
private Set<String> beanNames;
AuthenticationManagerDelegator(AuthenticationManagerBuilder delegateBuilder,
ApplicationContext context) {
Assert.notNull(delegateBuilder, "delegateBuilder cannot be null");
Field parentAuthMgrField = ReflectionUtils.findField(
AuthenticationManagerBuilder.class, "parentAuthenticationManager");
ReflectionUtils.makeAccessible(parentAuthMgrField);
beanNames = getAuthenticationManagerBeanNames(context);
validateBeanCycle(
ReflectionUtils.getField(parentAuthMgrField, delegateBuilder),
beanNames);
this.delegateBuilder = delegateBuilder;
}
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
if (delegate != null) {
// 如果被代理的 AuthenticationManager delegate 已经被构建则直接使用它进行认证
return delegate.authenticate(authentication);
}
synchronized (delegateMonitor) {
if (delegate == null) {
// 如果被代理的 AuthenticationManager delegate 尚未被构建,则在本次认证调用
// 中先对其进行构建,构建成功后忘掉所用的delegateBuilder
// 该模式中,这次认证也是对被代理的目标 AuthenticationManager 的首次认证调用
delegate = this.delegateBuilder.getObject();
this.delegateBuilder = null;
}
}
// 对目标 AuthenticationManager 的首次认证调用
return delegate.authenticate(authentication);
}
// 从指定应用上下文及其祖先上下文中查找类型为 AuthenticationManager 的 bean 的名称,可能有多个
private static Set<String> getAuthenticationManagerBeanNames(
ApplicationContext applicationContext) {
String[] beanNamesForType = BeanFactoryUtils
.beanNamesForTypeIncludingAncestors(applicationContext,
AuthenticationManager.class);
return new HashSet<>(Arrays.asList(beanNamesForType));
}
// 确保没有循环依赖
private static void validateBeanCycle(Object auth, Set<String> beanNames) {
if (auth != null && !beanNames.isEmpty()) {
if (auth instanceof Advised) {
Advised advised = (Advised) auth;
TargetSource targetSource = advised.getTargetSource();
if (targetSource instanceof LazyInitTargetSource) {
LazyInitTargetSource lits = (LazyInitTargetSource) targetSource;
if (beanNames.contains(lits.getTargetBeanName())) {
throw new FatalBeanException(
"A dependency cycle was detected when trying to resolve the AuthenticationManager. "
+ " Please ensure you have configured authentication.");
}
}
}
beanNames = Collections.emptySet();
}
}
}
static class DefaultPasswordEncoderAuthenticationManagerBuilder extends AuthenticationManagerBuilder {
private PasswordEncoder defaultPasswordEncoder;
/**
* Creates a new instance
*
* @param objectPostProcessor the 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(PasswordEncoder.class);
if (passwordEncoder == null) {
passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
this.passwordEncoder = passwordEncoder;
return passwordEncoder;
}
private <T> T getBeanOrNull(Class<T> type) {
try {
return this.applicationContext.getBean(type);
} catch(NoSuchBeanDefinitionException notFound) {
return null;
}
}
@Override
public String toString() {
return getPasswordEncoder().toString();
}
}
}