活动地址:CSDN21天学习挑战赛
前言
在上篇文章 SpringSecurity - 启动流程分析(五) 中,我们知道具体的认证逻辑是交给了 AuthenticationManager
来处理的,查看实现类可以看到只有一个 ProviderManager
实现类(其他都是内部类):
public class ProviderManager implements AuthenticationManager, MessageSourceAware,
InitializingBean {
private static final Log logger = LogFactory.getLog(ProviderManager.class);
private AuthenticationEventPublisher eventPublisher = new NullEventPublisher();
private List<AuthenticationProvider> providers = Collections.emptyList();
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
private AuthenticationManager parent;
private boolean eraseCredentialsAfterAuthentication = true;
public ProviderManager(AuthenticationProvider... providers) {
this(Arrays.asList(providers), null);
}
public ProviderManager(List<AuthenticationProvider> providers) {
this(providers, null);
}
public ProviderManager(List<AuthenticationProvider> providers,
AuthenticationManager parent) {
Assert.notNull(providers, "providers list cannot be null");
this.providers = providers;
this.parent = parent;
checkState();
}
public void afterPropertiesSet() {
checkState();
}
private void checkState() {
if (parent == null && providers.isEmpty()) {
throw new IllegalArgumentException(
"A parent AuthenticationManager or a list "
+ "of AuthenticationProviders is required");
} else if (providers.contains(null)) {
throw new IllegalArgumentException(
"providers list cannot contain null values");
}
}
// 核心认证方法
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
...
}
@SuppressWarnings("deprecation")
private void prepareException(AuthenticationException ex, Authentication auth) {
eventPublisher.publishAuthenticationFailure(ex, auth);
}
private void copyDetails(Authentication source, Authentication dest) {
if ((dest instanceof AbstractAuthenticationToken) && (dest.getDetails() == null)) {
AbstractAuthenticationToken token = (AbstractAuthenticationToken) dest;
token.setDetails(source.getDetails());
}
}
public List<AuthenticationProvider> getProviders() {
return providers;
}
public void setMessageSource(MessageSource messageSource) {
this.messages = new MessageSourceAccessor(messageSource);
}
public void setAuthenticationEventPublisher(
AuthenticationEventPublisher eventPublisher) {
Assert.notNull(eventPublisher, "AuthenticationEventPublisher cannot be null");
this.eventPublisher = eventPublisher;
}
public void setEraseCredentialsAfterAuthentication(boolean eraseSecretData) {
this.eraseCredentialsAfterAuthentication = eraseSecretData;
}
public boolean isEraseCredentialsAfterAuthentication() {
return eraseCredentialsAfterAuthentication;
}
private static final class NullEventPublisher implements AuthenticationEventPublisher {
public void publishAuthenticationFailure(AuthenticationException exception,
Authentication authentication) {
}
public void publishAuthenticationSuccess(Authentication authentication) {
}
}
}
我们大致看一下 ProviderManager
源码,可以看到其中一个变量:
private List<AuthenticationProvider> providers = Collections.emptyList();
这个变量存储了需要认证的 provider
。
接着查看一下核心方法:authenticate()
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
Class<? extends Authentication> toTest = authentication.getClass();
AuthenticationException lastException = null;
AuthenticationException parentException = null;
Authentication result = null;
Authentication parentResult = null;
boolean debug = logger.isDebugEnabled();
for (AuthenticationProvider provider : getProviders()) {
if (!provider.supports(toTest)) {
continue;
}
if (debug) {
logger.debug("Authentication attempt using "
+ provider.getClass().getName());
}
try {
result = provider.authenticate(authentication);
if (result != null) {
copyDetails(authentication, result);
break;
}
}
catch (AccountStatusException | InternalAuthenticationServiceException e) {
prepareException(e, authentication);
// SEC-546: Avoid polling additional providers if auth failure is due to
// invalid account status
throw e;
} catch (AuthenticationException e) {
lastException = e;
}
}
if (result == null && parent != null) {
// Allow the parent to try.
try {
result = parentResult = parent.authenticate(authentication);
}
catch (ProviderNotFoundException e) {
// ignore as we will throw below if no other exception occurred prior to
// calling parent and the parent
// may throw ProviderNotFound even though a provider in the child already
// handled the request
}
catch (AuthenticationException e) {
lastException = parentException = e;
}
}
if (result != null) {
if (eraseCredentialsAfterAuthentication
&& (result instanceof CredentialsContainer)) {
// Authentication is complete. Remove credentials and other secret data
// from authentication
((CredentialsContainer) result).eraseCredentials();
}
// If the parent AuthenticationManager was attempted and successful then it will publish an AuthenticationSuccessEvent
// This check prevents a duplicate AuthenticationSuccessEvent if the parent AuthenticationManager already published it
if (parentResult == null) {
eventPublisher.publishAuthenticationSuccess(result);
}
return result;
}
// Parent was null, or didn't authenticate (or throw an exception).
if (lastException == null) {
lastException = new ProviderNotFoundException(messages.getMessage(
"ProviderManager.providerNotFound",
new Object[] { toTest.getName() },
"No AuthenticationProvider found for {0}"));
}
// If the parent AuthenticationManager was attempted and failed then it will publish an AbstractAuthenticationFailureEvent
// This check prevents a duplicate AbstractAuthenticationFailureEvent if the parent AuthenticationManager already published it
if (parentException == null) {
prepareException(lastException, authentication);
}
throw lastException;
}
可以看到,authenticate()
方法中循环调用了 List<AuthenticationProvider>
中的 authenticate()
方法。
到这里为止我们知道了认证是交给了各种 AuthenticationProvider
的组合来处理的。查看 AuthenticationProvider
的实现类:
分析
接下来我们分析一下
SpringSecurity
是什么时候初始化AuthenticationManager
和ProviderManager
。
在 SpringSecurity - 启动流程分析(二) 这篇文章中,我们知道默认的过滤器链是通过调用 WebSecurityConfigurerAdapter
的 getHttp()
方法获取到 HttpSecurity
,只有由 HttpSecurity
生成过滤器链。我们来查看以下 getHttp()
方法:
protected final HttpSecurity getHttp() throws Exception {
...
// 核心方法,初始化 AuthenticationManager
AuthenticationManager authenticationManager = authenticationManager();
authenticationBuilder.parentAuthenticationManager(authenticationManager);
Map<Class<?>, Object> sharedObjects = createSharedObjects();
http = new HttpSecurity(objectPostProcessor, authenticationBuilder,
sharedObjects);
...
return http;
}
查看 authenticationManager()
方法:
protected AuthenticationManager authenticationManager() throws Exception {
if (!authenticationManagerInitialized) {
configure(localConfigureAuthenticationBldr);
if (disableLocalConfigureAuthenticationBldr) {
// 核心流程
authenticationManager = authenticationConfiguration
.getAuthenticationManager();
}
else {
authenticationManager = localConfigureAuthenticationBldr.build();
}
authenticationManagerInitialized = true;
}
return authenticationManager;
}
查看 AuthenticationConfiguration
的 getAuthenticationManager()
方法:
// 添加 GlobalAuthenticationConfigurerAdapter 类型的 Bean 到 IoC容器 中
@Bean
public static GlobalAuthenticationConfigurerAdapter enableGlobalAuthenticationAutowiredConfigurer(
ApplicationContext context) {
return new EnableGlobalAuthenticationAutowiredConfigurer(context);
}
// 添加 GlobalAuthenticationConfigurerAdapter 类型的 Bean 到 IoC容器 中
@Bean
public static InitializeUserDetailsBeanManagerConfigurer initializeUserDetailsBeanManagerConfigurer(ApplicationContext context) {
return new InitializeUserDetailsBeanManagerConfigurer(context);
}
// 添加 GlobalAuthenticationConfigurerAdapter 类型的 Bean 到 IoC容器 中
@Bean
public static InitializeAuthenticationProviderBeanManagerConfigurer initializeAuthenticationProviderBeanManagerConfigurer(ApplicationContext context) {
return new InitializeAuthenticationProviderBeanManagerConfigurer(context);
}
@Autowired(required = false)
public void setGlobalAuthenticationConfigurers(
List<GlobalAuthenticationConfigurerAdapter> configurers) {
configurers.sort(AnnotationAwareOrderComparator.INSTANCE);
this.globalAuthConfigurers = configurers;
}
@Bean
public AuthenticationManagerBuilder authenticationManagerBuilder(
ObjectPostProcessor<Object> objectPostProcessor, ApplicationContext context) {
// 初始化默认的 PasswordEncoder,跟踪源码可知是 DelegatingPasswordEncoder
LazyPasswordEncoder defaultPasswordEncoder = new LazyPasswordEncoder(context);
AuthenticationEventPublisher authenticationEventPublisher = getBeanOrNull(context, AuthenticationEventPublisher.class);
// 设置 AuthenticationManagerBuilder
DefaultPasswordEncoderAuthenticationManagerBuilder result = new DefaultPasswordEncoderAuthenticationManagerBuilder(objectPostProcessor, defaultPasswordEncoder);
if (authenticationEventPublisher != null) {
result.authenticationEventPublisher(authenticationEventPublisher);
}
return result;
}
public AuthenticationManager getAuthenticationManager() throws Exception {
if (this.authenticationManagerInitialized) {
return this.authenticationManager;
}
// 这里添加了默认的内部类 DefaultPasswordEncoderAuthenticationManagerBuilder 到 IoC容器 中
AuthenticationManagerBuilder authBuilder = this.applicationContext.getBean(AuthenticationManagerBuilder.class);
if (this.buildingAuthenticationManager.getAndSet(true)) {
return new AuthenticationManagerDelegator(authBuilder);
}
// 在 setGlobalAuthenticationConfigurers 中自动注入并排序
for (GlobalAuthenticationConfigurerAdapter config : globalAuthConfigurers) {
// 具体的逻辑,跟踪源码可以看到里面初始化了 DaoAuthenticationProvider
authBuilder.apply(config);
}
// 初始化完成 AuthenticationManager
authenticationManager = authBuilder.build();
if (authenticationManager == null) {
authenticationManager = getAuthenticationManagerBean();
}
this.authenticationManagerInitialized = true;
return authenticationManager;
}
通过以上源码跟踪,我们知道了默认会初始化一个 DaoAuthenticationProvider
作为 ProviderManager
中的 List<AuthenticationProvider>
属性的值。