AuthenticationManager 是 Spring Security 认证体系的核心引擎。本文深入拆解它的创建流程:AuthenticationConfiguration 如何自动加载全局配置器,InitializeUserDetailsBeanManagerConfigurer 如何自动创建 DaoAuthenticationProvider,以及 ProviderManager 的委托认证机制。
前言
在前面三篇文章中,我们理清了 Spring Security 的过滤器链是如何构建和执行的。但有一个关键组件一直在"幕后"------AuthenticationManager(认证管理器)。每次表单登录、每次 HTTP Basic 认证,最终都会走到 AuthenticationManager。
本文将回答三个问题:
AuthenticationManager是如何被创建的?DaoAuthenticationProvider为什么能"自动"出现?- Spring Boot 的
UserDetailsServiceAutoConfiguration在什么条件下才会创建内存用户?
一、AuthenticationConfiguration:全局认证配置中心
1.1 角色定位
AuthenticationConfiguration 是 Spring Security 自动配置的"认证中枢",它负责:
- 创建全局
AuthenticationManager - 加载全局认证配置器(
GlobalAuthenticationConfigurerAdapter) - 管理认证相关的 Bean 依赖(
PasswordEncoder、AuthenticationEventPublisher)
1.2 核心源码
java
@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;
@Bean
public AuthenticationManagerBuilder authenticationManagerBuilder(
ObjectPostProcessor<Object> objectPostProcessor,
ApplicationContext context) {
LazyPasswordEncoder defaultPasswordEncoder = new LazyPasswordEncoder(context);
AuthenticationEventPublisher eventPublisher =
getAuthenticationEventPublisher(context);
DefaultPasswordEncoderAuthenticationManagerBuilder result =
new DefaultPasswordEncoderAuthenticationManagerBuilder(
objectPostProcessor, defaultPasswordEncoder);
if (eventPublisher != null) {
result.authenticationEventPublisher(eventPublisher);
}
return result;
}
// 三个 static @Bean 全局配置器
@Bean
public static GlobalAuthenticationConfigurerAdapter
enableGlobalAuthenticationAutowiredConfigurer(ApplicationContext context) {
return new EnableGlobalAuthenticationAutowiredConfigurer(context);
}
@Bean
public static InitializeUserDetailsBeanManagerConfigurer
initializeUserDetailsBeanManagerConfigurer(ApplicationContext context) {
return new InitializeUserDetailsBeanManagerConfigurer(context);
}
@Bean
public static InitializeAuthenticationProviderBeanManagerConfigurer
initializeAuthenticationProviderBeanManagerConfigurer(
ApplicationContext context) {
return new InitializeAuthenticationProviderBeanManagerConfigurer(context);
}
// 收集所有 GlobalAuthenticationConfigurerAdapter 类型的 Bean
@Autowired(required = false)
public void setGlobalAuthenticationConfigurers(
List<GlobalAuthenticationConfigurerAdapter> configurers) {
configurers.sort(AnnotationAwareOrderComparator.INSTANCE);
this.globalAuthConfigurers = configurers;
}
}
1.3 getAuthenticationManager 方法
java
public AuthenticationManager getAuthenticationManager() throws Exception {
if (this.authenticationManagerInitialized) {
return this.authenticationManager; // 已构建,直接返回
}
// 从容器获取 AuthenticationManagerBuilder
AuthenticationManagerBuilder authBuilder =
this.applicationContext.getBean(AuthenticationManagerBuilder.class);
// 重入检测:防止循环依赖
if (this.buildingAuthenticationManager.getAndSet(true)) {
return new AuthenticationManagerDelegator(authBuilder); // 返回延迟代理
}
// 遍历所有全局配置器,依次 apply 到 builder
for (GlobalAuthenticationConfigurerAdapter config : this.globalAuthConfigurers) {
authBuilder.apply(config);
}
// 触发 build -> doBuild() -> init -> configure -> performBuild
this.authenticationManager = authBuilder.build();
if (this.authenticationManager == null) {
this.authenticationManager = getAuthenticationManagerBean(); // 兜底
}
this.authenticationManagerInitialized = true;
return this.authenticationManager;
}
关键点:
- 全局
AuthenticationManager是延迟初始化的,只有在第一次被请求时才会构建 - 构建过程:收集所有
GlobalAuthenticationConfigurerAdapterBean -> 按@Order排序 -> 依次apply到 builder ->build() - 内建重入防护 :通过
AtomicBoolean检测循环依赖,如检测到则返回AuthenticationManagerDelegator代理对象(延迟到首次authenticate()调用时才真正完成构建) - 兜底机制 :如果
build()返回 null(既无 provider 也无 parent),则通过lazyBean()延迟查找容器中唯一的AuthenticationManagerBean
1.4 DefaultPasswordEncoderAuthenticationManagerBuilder
AuthenticationConfiguration 内部定义了一个重要的子类,继承自 AuthenticationManagerBuilder:
java
static class DefaultPasswordEncoderAuthenticationManagerBuilder
extends AuthenticationManagerBuilder {
private PasswordEncoder 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);
}
}
该子类覆盖了所有需要 PasswordEncoder 的 DSL 方法,自动注入 LazyPasswordEncoder 作为默认编码器。而 LazyPasswordEncoder 在首次使用时才会从容器中查找唯一的 PasswordEncoder Bean(若无则使用 DelegatingPasswordEncoder 兜底)。
二、自动加载的全局配置器及其执行顺序
2.1 三个关键配置器
AuthenticationConfiguration 通过 static @Bean 声明了三个全局配置器,它们通过 @Autowired setGlobalAuthenticationConfigurers 被自动收集并按 @Order 排序后依次执行:
| 配置器 | @Order | 执行顺序 | 作用 |
|---|---|---|---|
EnableGlobalAuthenticationAutowiredConfigurer |
100(继承自父类 GlobalAuthenticationConfigurerAdapter) |
1 最先 | 通过 getBeansWithAnnotation(@EnableGlobalAuthentication) 提前初始化标注了该注解的 Bean |
InitializeAuthenticationProviderBeanManagerConfigurer |
LOWEST_PRECEDENCE - 5100 |
2 其次 | 查找容器中唯一的 AuthenticationProvider Bean -> 直接注册 |
InitializeUserDetailsBeanManagerConfigurer |
LOWEST_PRECEDENCE - 5000 |
3 最后 | 查找容器中唯一的 UserDetailsService Bean -> 自动创建 DaoAuthenticationProvider |
@Order 来源说明:
EnableGlobalAuthenticationAutowiredConfigurer没有显式标注@Order,但继承自GlobalAuthenticationConfigurerAdapter,后者在类上标注了@Order(100)。Spring 的AnnotationAwareOrderComparator会沿类继承链查找@Order,因此它能获得 100 的排序值。InitializeUserDetailsBeanManagerConfigurer自身标注@Order(InitializeUserDetailsBeanManagerConfigurer.DEFAULT_ORDER),其中DEFAULT_ORDER = Ordered.LOWEST_PRECEDENCE - 5000。InitializeAuthenticationProviderBeanManagerConfigurer自身标注@Order(InitializeAuthenticationProviderBeanManagerConfigurer.DEFAULT_ORDER),其中DEFAULT_ORDER = InitializeUserDetailsBeanManagerConfigurer.DEFAULT_ORDER - 100,即LOWEST_PRECEDENCE - 5100。注意 :这个值比InitializeUserDetailsBeanManagerConfigurer的 order 小 100,所以它先执行(order 值越小优先级越高)。
2.2 configure 方法的调用时机
调用链路:getAuthenticationManager() -> authBuilder.apply(config) -> authBuilder.build() -> doBuild()。doBuild() 在 AbstractConfiguredSecurityBuilder 中定义:
java
protected final O doBuild() throws Exception {
synchronized (this.configurers) {
this.buildState = BuildState.INITIALIZING;
beforeInit();
init(); // 1 init 阶段:调用所有 Configurer 的 init()
// -> 外部类 init() 内部 new 出内部 Configurer 并 apply
// -> 新 apply 的内部 Configurer 会在 while 循环中被再次 init()
this.buildState = BuildState.CONFIGURING;
beforeConfigure();
configure(); // 2 configure 阶段:调用所有 Configurer 的 configure()
// -> 内部类查找 Bean、创建 Provider、注册到 builder
this.buildState = BuildState.BUILDING;
O result = performBuild(); // 3 build 阶段:构建最终对象(ProviderManager)
this.buildState = BuildState.BUILT;
return result;
}
}
两阶段设计的关键代码 --init() 方法:
java
private void init() throws Exception {
Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
for (SecurityConfigurer<O, B> configurer : configurers) {
configurer.init((B) this);
}
// while 循环:处理在 init() 过程中新 apply 进来的 Configurer
while (!this.configurersAddedInInitializing.isEmpty()) {
List<SecurityConfigurer<O, B>> toInit = this.configurersAddedInInitializing;
this.configurersAddedInInitializing = new ArrayList<>();
for (SecurityConfigurer<O, B> configurer : toInit) {
configurer.init((B) this);
}
}
}
理解这个时序至关重要 :外部类的 init() 负责注册内部类(如 InitializeUserDetailsBeanManagerConfigurer.init() 中 auth.apply(new InitializeUserDetailsManagerConfigurer())),内部类的 configure() 负责真正的 Provider 创建逻辑。这种两阶段设计保证了所有 Configurer 都在 init 阶段完成注册,然后在 configure 阶段统一执行创建逻辑。
2.3 构建流程流程图

2.4 InitializeUserDetailsBeanManagerConfigurer 源码
java
@Order(InitializeUserDetailsBeanManagerConfigurer.DEFAULT_ORDER) // LOWEST_PRECEDENCE - 5000
class InitializeUserDetailsBeanManagerConfigurer
extends GlobalAuthenticationConfigurerAdapter {
static final int DEFAULT_ORDER = Ordered.LOWEST_PRECEDENCE - 5000;
private final ApplicationContext context;
@Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth.apply(new InitializeUserDetailsManagerConfigurer()); // 注册内部类
}
class InitializeUserDetailsManagerConfigurer
extends GlobalAuthenticationConfigurerAdapter {
private final Log logger = LogFactory.getLog(getClass());
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
String[] beanNames =
InitializeUserDetailsBeanManagerConfigurer.this.context
.getBeanNamesForType(UserDetailsService.class);
// 0. 守卫:优先检查 auth.isConfigured(),而非 beanNames
if (auth.isConfigured()) {
if (beanNames.length > 0) {
this.logger.warn(
"Global AuthenticationManager configured with an "
+ "AuthenticationProvider bean. UserDetailsService beans "
+ "will not be used ...");
}
return;
}
// 1. 查找 UserDetailsService Bean(要求恰好一个)
if (beanNames.length == 0) { return; }
if (beanNames.length > 1) {
this.logger.warn("Found " + beanNames.length
+ " UserDetailsService beans ...");
return;
}
UserDetailsService userDetailsService =
context.getBean(beanNames[0], UserDetailsService.class);
// 2. 查找可选依赖
PasswordEncoder passwordEncoder = getBeanOrNull(PasswordEncoder.class);
UserDetailsPasswordService passwordManager =
getBeanOrNull(UserDetailsPasswordService.class);
CompromisedPasswordChecker passwordChecker =
getBeanOrNull(CompromisedPasswordChecker.class);
// 3. 创建 DaoAuthenticationProvider(构造器注入 UserDetailsService)
DaoAuthenticationProvider provider =
new DaoAuthenticationProvider(userDetailsService);
if (passwordEncoder != null) {
provider.setPasswordEncoder(passwordEncoder);
}
if (passwordManager != null) {
provider.setUserDetailsPasswordService(passwordManager);
}
if (passwordChecker != null) {
provider.setCompromisedPasswordChecker(passwordChecker);
}
provider.afterPropertiesSet(); // 校验 UserDetailsService 等必填项
// 4. 注册到 AuthenticationManagerBuilder
auth.authenticationProvider(provider);
}
private <T> T getBeanOrNull(Class<T> type) {
return context.getBeanProvider(type).getIfUnique();
}
}
}
核心逻辑:
- 先检查
auth.isConfigured()-- 如果已有 Provider 注册则跳过(保护性守卫,并输出警告日志) - 要求容器中恰好一个
UserDetailsServiceBean(0 个跳过,>1 个警告跳过) - 构造器注入创建
DaoAuthenticationProvider - 注入可选依赖:
PasswordEncoder、UserDetailsPasswordService、CompromisedPasswordChecker - 调用
afterPropertiesSet()完成校验 -> 注册到 builder
注意 :源码中
isConfigured()的判断在beanNames获取之后 。这意味着即使已有 Provider 注册,只要容器中存在UserDetailsServiceBean,就会输出警告日志,提醒开发者注意潜在冲突。
2.5 InitializeAuthenticationProviderBeanManagerConfigurer
java
@Order(InitializeAuthenticationProviderBeanManagerConfigurer.DEFAULT_ORDER)
// LOWEST_PRECEDENCE - 5100
class InitializeAuthenticationProviderBeanManagerConfigurer
extends GlobalAuthenticationConfigurerAdapter {
static final int DEFAULT_ORDER =
InitializeUserDetailsBeanManagerConfigurer.DEFAULT_ORDER - 100;
private final ApplicationContext context;
@Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth.apply(new InitializeAuthenticationProviderManagerConfigurer());
}
class InitializeAuthenticationProviderManagerConfigurer
extends GlobalAuthenticationConfigurerAdapter {
@Override
public void configure(AuthenticationManagerBuilder auth) {
// 0. 守卫:已被其他配置器处理过则跳过
if (auth.isConfigured()) { return; }
// 1. 查找 AuthenticationProvider Bean(要求恰好一个)
String[] beanNames = context.getBeanNamesForType(
AuthenticationProvider.class);
if (beanNames.length == 0) { return; }
if (beanNames.length > 1) {
this.logger.info("Found " + beanNames.length
+ " AuthenticationProvider beans ...");
return;
}
// 2. 直接注册
AuthenticationProvider authenticationProvider =
context.getBean(beanNames[0], AuthenticationProvider.class);
auth.authenticationProvider(authenticationProvider);
}
}
}
与上一个配置器的区别:
| 维度 | InitializeUserDetailsBeanManagerConfigurer | InitializeAuthenticationProviderBeanManagerConfigurer |
|---|---|---|
| 查找的 Bean | UserDetailsService |
AuthenticationProvider |
| 创建的 Provider | 自动创建 DaoAuthenticationProvider |
直接使用已有 Bean |
| 适用场景 | 标准用户名密码登录 | 自定义认证逻辑(短信验证码、LDAP 等) |
| 执行顺序 | 3 最后(@Order LOWEST-5000) | 2 其次(@Order LOWEST-5100) |
执行顺序的重要性 :
InitAuthProvider的 order 是LOWEST-5100,比InitUserDetails的LOWEST-5000小 100,因此先执行 。如果它注册了 Provider,后续的InitUserDetails会通过auth.isConfigured()检测到并自动跳过(同时输出警告日志)。
2.6 AuthenticationManagerBuilder.isConfigured()
java
public boolean isConfigured() {
return !this.authenticationProviders.isEmpty()
|| this.parentAuthenticationManager != null;
}
只要 authenticationProviders 列表非空,或设置了 parentAuthenticationManager,builder 即视为"已配置"。performBuild() 也同样依赖此方法判断是否返回 null。
三、ProviderManager:认证管理器的核心实现
3.1 类结构
java
public class ProviderManager implements AuthenticationManager,
MessageSourceAware, InitializingBean {
private List<AuthenticationProvider> providers = Collections.emptyList();
private AuthenticationManager parent;
private AuthenticationEventPublisher eventPublisher = new NullEventPublisher();
private boolean eraseCredentialsAfterAuthentication = true;
}
3.2 构造与创建(performBuild)
ProviderManager 由 AuthenticationManagerBuilder.performBuild() 创建:
java
@Override
protected ProviderManager performBuild() throws Exception {
if (!isConfigured()) {
this.logger.debug("No authenticationProviders and no "
+ "parentAuthenticationManager defined. Returning null.");
return null;
}
ProviderManager providerManager = new ProviderManager(
this.authenticationProviders, this.parentAuthenticationManager);
if (this.eraseCredentials != null) {
providerManager.setEraseCredentialsAfterAuthentication(
this.eraseCredentials);
}
if (this.eventPublisher != null) {
providerManager.setAuthenticationEventPublisher(this.eventPublisher);
}
providerManager = postProcess(providerManager);
return providerManager;
}
3.3 authenticate 方法
java
@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;
int currentPosition = 0;
int size = this.providers.size();
// 1. 遍历所有 AuthenticationProvider
for (AuthenticationProvider provider : getProviders()) {
if (!provider.supports(toTest)) {
continue; // 不支持此类型,跳过
}
if (logger.isTraceEnabled()) {
logger.trace(LogMessage.format(
"Authenticating request with %s (%d/%d)",
provider.getClass().getSimpleName(),
++currentPosition, size));
}
try {
result = provider.authenticate(authentication);
if (result != null) {
copyDetails(authentication, result);
break; // 认证成功,跳出循环
}
} catch (AccountStatusException ex) {
// 账户状态异常:发布失败事件后立即终止
prepareException(ex, authentication);
throw ex;
} catch (InternalAuthenticationServiceException ex) {
// 内部服务异常:发布失败事件后立即终止
prepareException(ex, authentication);
throw ex;
} catch (AuthenticationException ex) {
// 普通认证失败(如 BadCredentialsException):记录后继续尝试下一个
ex.setAuthenticationRequest(authentication);
lastException = ex;
}
}
// 2. 所有 Provider 都无法认证,委托给父级
if (result == null && this.parent != null) {
try {
parentResult = this.parent.authenticate(authentication);
result = parentResult;
} catch (ProviderNotFoundException ex) {
// 父级也找不到,忽略(后续统一抛异常)
} catch (AuthenticationException ex) {
parentException = ex;
lastException = ex;
}
}
// 3. 认证成功:清除凭据 -> 发布事件 -> 返回
if (result != null) {
if (this.eraseCredentialsAfterAuthentication
&& (result instanceof CredentialsContainer)) {
((CredentialsContainer) result).eraseCredentials();
}
// 仅在非父级认证成功时发布事件(防止重复)
if (parentResult == null) {
this.eventPublisher.publishAuthenticationSuccess(result);
}
return result;
}
// 4. 认证失败
if (lastException == null) {
lastException = new ProviderNotFoundException(
this.messages.getMessage("ProviderManager.providerNotFound",
new Object[] { toTest.getName() },
"No AuthenticationProvider found for {0}"));
}
// 仅在非父级抛出异常时发布失败事件(防止重复)
if (parentException == null) {
prepareException(lastException, authentication);
}
throw lastException;
}
// 辅助方法:发布失败事件 + 设置 authentication 到异常中
private void prepareException(AuthenticationException ex,
Authentication auth) {
ex.setAuthenticationRequest(auth);
this.eventPublisher.publishAuthenticationFailure(ex, auth);
}
3.4 异常处理策略
ProviderManager 对 AuthenticationException 的子类采用了不同的处理策略:
| 异常类型 | 处理策略 | 原因 |
|---|---|---|
AccountStatusException |
立即抛出,发布失败事件 | 账户锁定/禁用是终局判断,换 Provider 也没用 |
InternalAuthenticationServiceException |
立即抛出,发布失败事件 | 系统内部错误(UserDetailsService 异常等),不应继续 |
BadCredentialsException 等普通异常 |
记录下来,继续尝试下一个 Provider | 换个 Provider 可能认证成功(如 LDAP -> 数据库) |
ProviderNotFoundException(来自父级) |
静默忽略 | 父级没有对应 Provider 是正常的 |
3.5 copyDetails 方法
java
private void copyDetails(Authentication source, Authentication dest) {
if ((dest instanceof AbstractAuthenticationToken token)
&& (dest.getDetails() == null)) {
token.setDetails(source.getDetails());
}
}
仅当目标为 AbstractAuthenticationToken 且其 details 为空时才复制,保留目标可能已有的 details。
3.6 认证流程图

四、DaoAuthenticationProvider 的核心逻辑
检索用户与密码校验
java
public class DaoAuthenticationProvider
extends AbstractUserDetailsAuthenticationProvider {
// 注意:passwordEncoder 是一个 Supplier,通过 SingletonSupplier 包装
private Supplier<PasswordEncoder> passwordEncoder = SingletonSupplier
.of(PasswordEncoderFactories::createDelegatingPasswordEncoder);
private UserDetailsService userDetailsService;
private UserDetailsPasswordService userDetailsPasswordService;
private CompromisedPasswordChecker compromisedPasswordChecker;
@Override
protected final UserDetails retrieveUser(String username,
UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
prepareTimingAttackProtection(); // 准备时序攻击防护
try {
// 1. 调用 UserDetailsService 查询用户
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 void additionalAuthenticationChecks(UserDetails userDetails,
UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
if (authentication.getCredentials() == null) {
throw new BadCredentialsException("Bad credentials"); // 无凭据
}
// 2. 校验密码(注意 passwordEncoder 是 Supplier,需 .get() 获取实例)
String presentedPassword = authentication.getCredentials().toString();
if (!this.passwordEncoder.get().matches(
presentedPassword, userDetails.getPassword())) {
throw new BadCredentialsException("Bad credentials");
}
}
}
关键细节:
passwordEncoder字段类型是Supplier<PasswordEncoder>(由SingletonSupplier包装),因此调用时需使用passwordEncoder.get().matches()而非passwordEncoder.matches()- 默认的
PasswordEncoder通过PasswordEncoderFactories.createDelegatingPasswordEncoder()懒加载创建 retrieveUser()对Exception(非 Security 异常)做了统一包装为InternalAuthenticationServiceException
五、实战:自定义 UserDetailsService
在大多数生产环境中,用户信息存储在数据库中,需要自定义 UserDetailsService:
java
@Service
public class CustomUserDetailsService implements UserDetailsService {
@Autowired
private UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
User user = userMapper.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException(
"User not found: " + username));
return org.springframework.security.core.userdetails.User
.withUsername(user.getUsername())
.password(user.getPassword())
.authorities(user.getRoles().stream()
.map(role -> "ROLE_" + role.getName())
.toArray(String[]::new))
.build();
}
}
效果:
InitializeUserDetailsBeanManagerConfigurer检测到CustomUserDetailsService- 自动创建
DaoAuthenticationProvider,注入userDetailsService+ 容器中的PasswordEncoder - Spring Boot 的
UserDetailsServiceAutoConfiguration中@ConditionalOnMissingBean(UserDetailsService.class)条件不再满足 -> 不创建内存用户 - 容器中最终只有一个
DaoAuthenticationProvider,由ProviderManager管理
六、总结
| 组件 | 职责 | 创建条件 |
|---|---|---|
AuthenticationConfiguration |
全局认证配置中心,声明 3 个全局配置器 + 管理 build 流程 | @Configuration,Spring 自动扫描 |
EnableGlobalAuthenticationAutowiredConfigurer |
提前初始化 @EnableGlobalAuthentication 标注的 Bean |
始终存在(@Order 100,来自父类,最先执行) |
InitializeAuthenticationProviderBeanManagerConfigurer |
查找唯一的 AuthenticationProvider Bean -> 直接注册 |
容器中恰好一个 AuthenticationProvider Bean(@Order LOWEST-5100) |
InitializeUserDetailsBeanManagerConfigurer |
查找唯一的 UserDetailsService Bean -> 创建 DaoAuthenticationProvider |
容器中恰好一个 UserDetailsService Bean(@Order LOWEST-5000) |
ProviderManager |
遍历 AuthenticationProvider 执行认证,含异常分类处理和父级回退 |
performBuild() 构建(providers 为空且无 parent 时返回 null) |
DaoAuthenticationProvider |
使用 UserDetailsService 查询用户 + PasswordEncoder 校验密码 |
由 InitializeUserDetailsBeanManagerConfigurer 自动创建 |
build 构建生命周期

下一篇预告:《Spring Security 表单登录认证全流程:UsernamePasswordAuthenticationFilter 源码拆解》将深入分析从用户提交登录表单到认证成功的完整源码链路。