1,简要说明
在Spring Security中对用户进行认证的是AuthenticationManager,其只有一个方法,尝试对封装了认证信息的Authentication进行身份验证,如果成功,则返回完全填充的Authentication(包括授予的权限)。
public interface AuthenticationManager {
/**
* 尝试对通过Authentication实例对象封装的身份信息进行验证。
* 如果验证成功,则返回完全填充的Authentication对象(包括授予的权限)。
*
* AuthenticationManager 建议遵循以下的约定
* 1,如果帐户被禁用并且AuthenticationManager可以测试此状态,则必须引发 DisabledException
* 2,如果帐户被锁定并且并且AuthenticationManager可以测试帐户锁定,则必须抛出LockedException
* 3,如果凭据不正确,则必须抛出BadCredentialsException
* 虽然上述选项是可选的,但是 AuthenticationManager 必须始终测试凭据。
* 我们应该上述顺序捕获这些异常,同时实现者也应按上述顺序抛出异常(即,如果帐户被禁用或锁定,
* 则立即拒绝身份验证请求,并且不执行凭据测试过程),这可以防止根据禁用或锁定的帐户测试凭据。
*/
Authentication authenticate(Authentication authentication)
throws AuthenticationException;
}
AuthenticationManager 只关注认证成功与否而并不关心具体的认证方式。例如我们可以通过用户名及密码、短信、刷脸、OAuth2协议等方式进行认证。对于这些具体认证方式是交给了AuthenticationProvider来负责。
public interface AuthenticationProvider {
/**
*使用与AuthenticationManager的authenticate方法相同的
*协定执行身份验证(例如按照什么规则抛出异常等)
*/
Authentication authenticate(Authentication authentication)
throws AuthenticationException;
/**
*如果支持指定的Authentication 对象,则返回true</code
*/
boolean supports(Class<?> authentication);
}
下面展示了AuthenticationProvider的部分实现
AuthenticationManager与AuthenticationProvider 是怎么被创建使用,以及如何自由的添加认证方式的问题,在下面的内容中将进一步分析
2,认证方式的全局配置
前面章节分析了WebSecurityConfiguration配置类里面相关的内容,现在回到@EnableWebSecurity 注解上,我们接着分析下@EnableGlobalAuthentication内部都做了些啥
@Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
@Target(value = { java.lang.annotation.ElementType.TYPE })
@Documented
@Import({ WebSecurityConfiguration.class,
SpringWebMvcImportSelector.class,
OAuth2ImportSelector.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
@Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
@Target(value = { java.lang.annotation.ElementType.TYPE })
@Documented
@Import(AuthenticationConfiguration.class)
@Configuration
public @interface EnableGlobalAuthentication {
}
其重点就是导入了AuthenticationConfiguration配置对象
@Configuration(proxyBeanMethods = false)
@Import(ObjectPostProcessorConfiguration.class)
public class AuthenticationConfiguration {
... ...
}
AuthenticationConfiguration 中还导入了ObjectPostProcessorConfiguration配置,该配置比较简单,就是实例化了一个bean,而该Bean在前面的章节中也在不断的用到
@Configuration(proxyBeanMethods = false)
public class ObjectPostProcessorConfiguration {
@Bean
public ObjectPostProcessor<Object> objectPostProcessor(
AutowireCapableBeanFactory beanFactory) {
return new AutowireBeanFactoryObjectPostProcessor(beanFactory);
}
}
3,AuthenticationConfiguration
下面,我们深入分析下AuthenticationConfiguration配置类的实现。
先简单的说下AuthenticationManager构建的主体过程
- AuthenticationConfiguration中收集所有GlobalAuthenticationConfigurerAdapter类型的Bean并保存到globalAuthConfigurers中;
- AuthenticationConfiguration中创建AuthenticationManagerBuilder类型的实例对象DefaultPasswordEncoderAuthenticationManagerBuilder;
- 将globalAuthConfigurers里面的对象传递给AuthenticationManagerBuilder;
- 执行AuthenticationManagerBuilder的build()方法完成AuthenticationManager的构建;
- WebSecurityConfigurerAdapter中调用AuthenticationConfiguration的getAuthenticationManager()方法得到构建的AuthenticationManager类似对象;
@Configuration(proxyBeanMethods = false)
@Import(ObjectPostProcessorConfiguration.class)
public class AuthenticationConfiguration {
private AtomicBoolean buildingAuthenticationManager = new AtomicBoolean();
private ApplicationContext applicationContext;
private AuthenticationManager authenticationManager;
private boolean authenticationManagerInitialized;
private List<GlobalAuthenticationConfigurerAdapter> globalAuthConfigurers = Collections
.emptyList();
private ObjectPostProcessor<Object> objectPostProcessor;
@Autowired
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@Autowired
public void setObjectPostProcessor(ObjectPostProcessor<Object> objectPostProcessor) {
this.objectPostProcessor = objectPostProcessor;
}
//收集GlobalAuthenticationConfigurerAdapter类型对象
@Autowired(required = false)
public void setGlobalAuthenticationConfigurers(
List<GlobalAuthenticationConfigurerAdapter> configurers) {
configurers.sort(AnnotationAwareOrderComparator.INSTANCE);
this.globalAuthConfigurers = configurers;
}
@Bean
public static GlobalAuthenticationConfigurerAdapter enableGlobalAuthenticationAutowiredConfigurer(
ApplicationContext context) {
//GlobalAuthenticationConfigurerAdapter类型对象
return new EnableGlobalAuthenticationAutowiredConfigurer(context);
}
//目的是构建默认的DaoAuthenticationProvider
@Bean
public static InitializeUserDetailsBeanManagerConfigurer initializeUserDetailsBeanManagerConfigurer(ApplicationContext context) {
//GlobalAuthenticationConfigurerAdapter类型对象
return new InitializeUserDetailsBeanManagerConfigurer(context);
}
//目的是将ApplicationContext中存在的AuthenticationProvider类型直接添加进来
@Bean
public static InitializeAuthenticationProviderBeanManagerConfigurer initializeAuthenticationProviderBeanManagerConfigurer(ApplicationContext context) {
//GlobalAuthenticationConfigurerAdapter类型对象
return new InitializeAuthenticationProviderBeanManagerConfigurer(context);
}
//身份验证管理器生成器【下面个方法会获取使用】
@Bean
public AuthenticationManagerBuilder authenticationManagerBuilder(
ObjectPostProcessor<Object> objectPostProcessor, ApplicationContext context) {
//创建“惰性”密码编码器,所谓“惰性”是指在真正使用到某个方法时才去到ApplicationContext中
//获取PasswordEncoder类型的对象进行调用【对于不确定某对象何时加入到ApplicationContext是挺有用】
//默认情况,如果ApplicationContext没有得到PasswordEncoder类型的实例,
//则使用默认的DelegatingPasswordEncoder
LazyPasswordEncoder defaultPasswordEncoder = new LazyPasswordEncoder(context);
//从ApplicationContext中获取AuthenticationEventPublisher类型的bean
AuthenticationEventPublisher authenticationEventPublisher = getBeanOrNull(context, AuthenticationEventPublisher.class);
//创建默认的AuthenticationManagerBuilder实例
DefaultPasswordEncoderAuthenticationManagerBuilder result = new DefaultPasswordEncoderAuthenticationManagerBuilder(objectPostProcessor, defaultPasswordEncoder);
if (authenticationEventPublisher != null) {
result.authenticationEventPublisher(authenticationEventPublisher);
}
return result;
}
//创建身份认证管理器实例
//注意:该方法在WebSecurityConfigurerAdapter中被调用
public AuthenticationManager getAuthenticationManager() throws Exception {
if (this.authenticationManagerInitialized) {
return this.authenticationManager;
}
//获取身份验证管理器生成器DefaultPasswordEncoderAuthenticationManagerBuilder
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;
}
private AuthenticationManager getAuthenticationManagerBean() {
return lazyBean(AuthenticationManager.class);
}
}
3.1 ,DefaultPasswordEncoderAuthenticationManagerBuilder
由上门可知,其默认使用DefaultPasswordEncoderAuthenticationManagerBuilder作为认证管理的构建器,下面分析其build()方法的执行过程。
DefaultPasswordEncoderAuthenticationManagerBuilder在执行build()方法时,其父类AbstractConfiguredSecurityBuilder的doBuild()方法被执行,前面有说过,这个方法是个模板方法,如下所示:
public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBuilder<O>>
extends AbstractSecurityBuilder<O> {
private final Log logger = LogFactory.getLog(getClass());
private final LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>> configurers = new LinkedHashMap<>();
private final List<SecurityConfigurer<O, B>> configurersAddedInInitializing = new ArrayList<>();
private final Map<Class<?>, Object> sharedObjects = new HashMap<>();
private final boolean allowConfigurersOfSameType;
private BuildState buildState = BuildState.UNBUILT;
private ObjectPostProcessor<Object> objectPostProcessor;
//模板方法
protected final O doBuild() throws Exception {
synchronized (configurers) {
buildState = BuildState.INITIALIZING;
beforeInit();
init(); //这个方法的执行是关键
buildState = BuildState.CONFIGURING;
beforeConfigure();
configure();
buildState = BuildState.BUILDING;
O result = performBuild(); //执行子类的具体实现
buildState = BuildState.BUILT;
return result;
}
}
protected abstract O performBuild() throws Exception;
@SuppressWarnings("unchecked")
private void init() throws Exception {
//当前这里存储的是GlobalAuthenticationConfigurerAdapter类型的实例,
//默认的有:InitializeUserDetailsBeanManagerConfigurer、
//InitializeAuthenticationProviderBeanManagerConfigurer、
//EnableGlobalAuthenticationAutowiredConfigurer
Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
for (SecurityConfigurer<O, B> configurer : configurers) {
configurer.init((B) this);
}
for (SecurityConfigurer<O, B> configurer : configurersAddedInInitializing) {
configurer.init((B) this);
}
}
@SuppressWarnings("unchecked")
private void configure() throws Exception {
//当前这里存储的是GlobalAuthenticationConfigurerAdapter类型的实例,
//默认的有:InitializeUserDetailsBeanManagerConfigurer、
//InitializeAuthenticationProviderBeanManagerConfigurer、
//EnableGlobalAuthenticationAutowiredConfigurer
Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
for (SecurityConfigurer<O, B> configurer : configurers) {
configurer.configure((B) this);
}
}
}
接着我们分析下这三个默认的GlobalAuthenticationConfigurerAdapter类型的实例中init和configure方法都做了啥
3.2 InitializeUserDetailsBeanManagerConfigurer
该类的目的纯粹是为了添加InitializeUserDetailsManagerConfigurer配置,通过在其configure方法阶段创建DaoAuthenticationProvider对象,最终被添加到ProviderManager中
@Order(InitializeUserDetailsBeanManagerConfigurer.DEFAULT_ORDER)
class InitializeUserDetailsBeanManagerConfigurer
extends GlobalAuthenticationConfigurerAdapter {
static final int DEFAULT_ORDER = Ordered.LOWEST_PRECEDENCE - 5000;
private final ApplicationContext context;
/**
* @param context
*/
InitializeUserDetailsBeanManagerConfigurer(ApplicationContext context) {
this.context = context;
}
//注意:这里并没有重写configure方法
@Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
//直接添加InitializeUserDetailsManagerConfigurer配置类
auth.apply(new InitializeUserDetailsManagerConfigurer());
}
class InitializeUserDetailsManagerConfigurer
extends GlobalAuthenticationConfigurerAdapter {
//重写configure方法
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
if (auth.isConfigured()) {
return;
}
//用于根据用户名得到用户信息
//Springboot的自动化配置中会默认创建InMemoryUserDetailsManager
UserDetailsService userDetailsService = getBeanOrNull(
UserDetailsService.class);
if (userDetailsService == null) {
return;
}
//用于对用户的密码进行加密以及密码比对验证
PasswordEncoder passwordEncoder = getBeanOrNull(PasswordEncoder.class);
//用户更新用户密码
UserDetailsPasswordService passwordManager = getBeanOrNull(UserDetailsPasswordService.class);
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(userDetailsService);
if (passwordEncoder != null) {
provider.setPasswordEncoder(passwordEncoder);
}
if (passwordManager != null) {
provider.setUserDetailsPasswordService(passwordManager);
}
//用于验证provider中的userDetailsService是否为空【不许为空】
provider.afterPropertiesSet();
//直接添加回DefaultPasswordEncoderAuthenticationManagerBuilder
auth.authenticationProvider(provider);
}
/**
* @return a bean of the requested class if there's just a single registered component, null otherwise.
*/
private <T> T getBeanOrNull(Class<T> type) {
String[] userDetailsBeanNames = InitializeUserDetailsBeanManagerConfigurer.this.context
.getBeanNamesForType(type);
if (userDetailsBeanNames.length != 1) {
return null;
}
return InitializeUserDetailsBeanManagerConfigurer.this.context
.getBean(userDetailsBeanNames[0], type);
}
}
}
Springboot的自动化配置中会默认创建InMemoryUserDetailsManager,请参考Spring Security解析二:自动化装配
我们也可以通过配置来指定,例如:
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.jdbcAuthentication()
.dataSource(dataSource)
.withDefaultSchema()
.withUser("user").password("password").roles("USER").and()
.withUser("admin").password("password").roles("USER", "ADMIN");
}
接着进一步研究下DaoAuthenticationProvider都做了些啥,它是怎么对身份进行认证的?
3.2.1 AbstractUserDetailsAuthenticationProvider
public abstract class AbstractUserDetailsAuthenticationProvider implements
AuthenticationProvider, InitializingBean, MessageSourceAware {
protected final Log logger = LogFactory.getLog(getClass());
// ~ Instance fields
// ================================================================================================
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
private UserCache userCache = new NullUserCache();
private boolean forcePrincipalAsString = false;
protected boolean hideUserNotFoundExceptions = true;
private UserDetailsChecker preAuthenticationChecks = new DefaultPreAuthenticationChecks();
private UserDetailsChecker postAuthenticationChecks = new DefaultPostAuthenticationChecks();
private GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper();
protected abstract void additionalAuthenticationChecks(UserDetails userDetails,
UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException;
protected abstract UserDetails retrieveUser(String username,
UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException;
//说明该身份认证方式仅适合UsernamePasswordAuthenticationToken
@Override
public boolean supports(Class<?> authentication) {
return (UsernamePasswordAuthenticationToken.class
.isAssignableFrom(authentication));
}
@Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication,
() -> messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.onlySupports",
"Only UsernamePasswordAuthenticationToken is supported"));
// 从认证主体中得到用户名(或返回未提供-NONE_PROVIDED)
String username = (authentication.getPrincipal() == null) ? "NONE_PROVIDED"
: authentication.getName();
boolean cacheWasUsed = true;
//从缓存中取得用户信息(默认使用的缓存是NullUserCache)
UserDetails user = this.userCache.getUserFromCache(username);
if (user == null) {
cacheWasUsed = false;
try {
//检索用户【由子类实现】
user = retrieveUser(username,
(UsernamePasswordAuthenticationToken) authentication);
}
catch (UsernameNotFoundException notFound) {
logger.debug("User '" + username + "' not found");
if (hideUserNotFoundExceptions) {
throw new BadCredentialsException(messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.badCredentials",
"Bad credentials"));
}
else {
throw notFound;
}
}
Assert.notNull(user,
"retrieveUser returned null - a violation of the interface contract");
}
try {
//检测UserDetails的状态【是否被锁、是否可用、是否过期】
preAuthenticationChecks.check(user);
//子类执行任何附加检查,例如:验证密码是否匹配
additionalAuthenticationChecks(user,
(UsernamePasswordAuthenticationToken) authentication);
}
catch (AuthenticationException exception) {
if (cacheWasUsed) {
//如果从缓存中得到的用户信息验证失败,则从非缓存的地方得到重新验证
// There was a problem, so try again after checking
// we're using latest data (i.e. not from the cache)
cacheWasUsed = false;
user = retrieveUser(username,
(UsernamePasswordAuthenticationToken) authentication);
preAuthenticationChecks.check(user);
additionalAuthenticationChecks(user,
(UsernamePasswordAuthenticationToken) authentication);
}
else {
throw exception;
}
}
//检测UserDetails的状态【凭据是否未过期】
postAuthenticationChecks.check(user);
if (!cacheWasUsed) {
//将通过检查的用户信息加入缓存
this.userCache.putUserInCache(user);
}
Object principalToReturn = user;
if (forcePrincipalAsString) { //值返回用户名
principalToReturn = user.getUsername();
}
//返回成功的响应
return createSuccessAuthentication(principalToReturn, authentication, user);
}
//注意:子类有重写
protected Authentication createSuccessAuthentication(Object principal,
Authentication authentication, UserDetails user) {
// Ensure we return the original credentials the user supplied,
// so subsequent attempts are successful even with encoded passwords.
// Also ensure we return the original getDetails(), so that future
// authentication events after cache expiry contain the details
UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(
principal, authentication.getCredentials(),
authoritiesMapper.mapAuthorities(user.getAuthorities()));
result.setDetails(authentication.getDetails());
return result;
}
}
可见上的操作主要是从某个地方得到用户信息,然后检查用户的状态,如果检查失败则抛出相应的异常,否则返回成功的认证信息。
上面的retrieveUser与additionalAuthenticationChecks是需要继续研究的地方
3.2.2 DaoAuthenticationProvider
public class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
private PasswordEncoder passwordEncoder;
private UserDetailsService userDetailsService;
private UserDetailsPasswordService userDetailsPasswordService;
public DaoAuthenticationProvider() {
setPasswordEncoder(PasswordEncoderFactories.createDelegatingPasswordEncoder());
}
@SuppressWarnings("deprecation")
@Override
protected void additionalAuthenticationChecks(UserDetails userDetails,
UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
if (authentication.getCredentials() == null) {
logger.debug("Authentication failed: no credentials provided");
throw new BadCredentialsException(messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.badCredentials",
"Bad credentials"));
}
String presentedPassword = authentication.getCredentials().toString();
if (!passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
logger.debug("Authentication failed: password does not match stored value");
throw new BadCredentialsException(messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.badCredentials",
"Bad credentials"));
}
}
@Override
protected final UserDetails retrieveUser(String username,
UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
prepareTimingAttackProtection();
try {
UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
if (loadedUser == null) {
throw new InternalAuthenticationServiceException(
"UserDetailsService returned null, which is an interface contract violation");
}
return loadedUser;
}
catch (UsernameNotFoundException ex) {
mitigateAgainstTimingAttack(authentication);
throw ex;
}
catch (InternalAuthenticationServiceException ex) {
throw ex;
}
catch (Exception ex) {
throw new InternalAuthenticationServiceException(ex.getMessage(), ex);
}
}
@Override
protected Authentication createSuccessAuthentication(Object principal,
Authentication authentication, UserDetails user) {
boolean upgradeEncoding = this.userDetailsPasswordService != null
&& this.passwordEncoder.upgradeEncoding(user.getPassword());
if (upgradeEncoding) {
String presentedPassword = authentication.getCredentials().toString();
String newPassword = this.passwordEncoder.encode(presentedPassword);
user = this.userDetailsPasswordService.updatePassword(user, newPassword);
}
return super.createSuccessAuthentication(principal, authentication, user);
}
}
上面通过userDetailsService来得到用户的信息,并通过passwordEncoder来验证密码是否正确,而这两个对象是通过上面 3.2小结里的InitializeUserDetailsManagerConfigurer中从ApplicationContext获得。
3.3 InitializeAuthenticationProviderBeanManagerConfigurer
该类的目的纯粹是为了添加InitializeUserDetailsManagerConfigurer配置,通过在其configure方法阶段从ApplicationContext中得到AuthenticationProvider类型的Bean,并加入到ProviderManager中
@Order(InitializeAuthenticationProviderBeanManagerConfigurer.DEFAULT_ORDER)
class InitializeAuthenticationProviderBeanManagerConfigurer
extends GlobalAuthenticationConfigurerAdapter {
static final int DEFAULT_ORDER = InitializeUserDetailsBeanManagerConfigurer.DEFAULT_ORDER
- 100;
private final ApplicationContext context;
/**
* @param context the ApplicationContext to look up beans.
*/
InitializeAuthenticationProviderBeanManagerConfigurer(
ApplicationContext context) {
this.context = context;
}
@Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth.apply(new InitializeUserDetailsManagerConfigurer());
}
class InitializeUserDetailsManagerConfigurer
extends GlobalAuthenticationConfigurerAdapter {
@Override
public void configure(AuthenticationManagerBuilder auth) {
if (auth.isConfigured()) {
return;
}
//直接从ApplicationContext中得到AuthenticationProvider类型Bean
//也就是说,我们可以自定义一个,直接加入到ApplicationContext就可生效了,方便扩展
AuthenticationProvider authenticationProvider = getBeanOrNull(
AuthenticationProvider.class);
if (authenticationProvider == null) {
return;
}
//这里直接添加到DefaultPasswordEncoderAuthenticationManagerBuilder中
auth.authenticationProvider(authenticationProvider);
}
/**
* @return
*/
private <T> T getBeanOrNull(Class<T> type) {
String[] userDetailsBeanNames = InitializeAuthenticationProviderBeanManagerConfigurer.this.context
.getBeanNamesForType(type);
if (userDetailsBeanNames.length != 1) {
return null;
}
return InitializeAuthenticationProviderBeanManagerConfigurer.this.context
.getBean(userDetailsBeanNames[0], type);
}
}
}
小结:
Spring Security默认给我们创建了一个支持UsernamePasswordAuthenticationToken认证的AuthenticationProvider,里面用到了从ApplicationContext中取到的UserDetailsService、PasswordEncoder和UserDetailsPasswordService的实例对象;
我们可以自定义AuthenticationProvider实例来添加其它类型的验证工作,同时,只需要将实例对象添加到ApplicationContext容器中即可生效。
经过上来的步骤后,在DefaultPasswordEncoderAuthenticationManagerBuilder的authenticationProviders属性中添加了一个或多个AuthenticationProvider,接下来的工作便是执行DefaultPasswordEncoderAuthenticationManagerBuilder的performBuild()方法完成AuthenticationManager的创建工作。当然,其实该方法是在父类AuthenticationManagerBuilder中的。
public class AuthenticationManagerBuilder
extends
AbstractConfiguredSecurityBuilder<AuthenticationManager, AuthenticationManagerBuilder>
implements ProviderManagerBuilder<AuthenticationManagerBuilder> {
private final Log logger = LogFactory.getLog(getClass());
private AuthenticationManager parentAuthenticationManager;
private List<AuthenticationProvider> authenticationProviders = new ArrayList<>();
private UserDetailsService defaultUserDetailsService;
private Boolean eraseCredentials;
private AuthenticationEventPublisher eventPublisher;
@Override
protected ProviderManager performBuild() throws Exception {
if (!isConfigured()) {
logger.debug("No authenticationProviders and no parentAuthenticationManager defined. Returning null.");
return null;
}
ProviderManager providerManager = new ProviderManager(authenticationProviders,
parentAuthenticationManager);
if (eraseCredentials != null) {
providerManager.setEraseCredentialsAfterAuthentication(eraseCredentials);
}
if (eventPublisher != null) {
providerManager.setAuthenticationEventPublisher(eventPublisher);
}
providerManager = postProcess(providerManager);
return providerManager;
}
}
返回的其实是ProviderManager,而ProviderManager可以看成是AuthenticationManager的代理对象,里面保存了多个AuthenticationManager的实现。
public class ProviderManager implements AuthenticationManager, MessageSourceAware,
InitializingBean {
// ~ Static fields/initializers
// =====================================================================================
private static final Log logger = LogFactory.getLog(ProviderManager.class);
// ~ Instance fields
// ================================================================================================
private AuthenticationEventPublisher eventPublisher = new NullEventPublisher();
private List<AuthenticationProvider> providers = Collections.emptyList();
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
//可以是ProviderManager,目的是当无法进行身份认证时,再使用这个AuthenticationManager进行认证
private AuthenticationManager parent;
private boolean eraseCredentialsAfterAuthentication = true;
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");
}
}
@Override
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();
//查找匹配的 AuthenticationProvider 来执行验证
for (AuthenticationProvider provider : getProviders()) {
if (!provider.supports(toTest)) {
continue;
}
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 than 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 than 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;
}
}
Spring Security默认情况下为我们创建了一个基于用户名和密码进行验证的AuthenticationManager实例,同时收集ApplicationContext中的AuthenticationProvider类型的Bean 一起添加到ProviderManager(AuthenticationManager的子类)中供需要的地方进行使用。