1. 简介
本文将深入介绍 Spring Security 中的 AuthenticationManagerResolver
,并通过实际示例展示如何在 Basic 认证 和 OAuth2 认证 场景中灵活使用它。
这个功能特别适合多租户、多用户体系的系统,能让你根据不同请求上下文动态选择认证逻辑,避免“一把钥匙开所有门”的尴尬。
2. 什么是 AuthenticationManager?
简单来说,AuthenticationManager
是 Spring Security 中负责认证的核心接口。它的 authenticate()
方法接收一个 Authentication
对象,验证通过后返回一个已认证(authenticated=true
)的 Authentication
实例。
- ✅ 认证成功:返回带有凭证的
Authentication
对象 - ❌ 认证失败:抛出
AuthenticationException
- ⚠️ 无法判断:返回
null
(表示不处理)
最常见的实现是 ProviderManager
,它会将认证请求委派给一个 AuthenticationProvider
列表,直到某个 Provider 成功处理。
在配置层面,你可以通过以下方式设置 AuthenticationManager
:
- 全局配置:直接定义一个
AuthenticationManager
Bean - 局部配置:在
SecurityFilterChain
中通过HttpSecurity
获取AuthenticationManagerBuilder
来构建
AuthenticationManagerBuilder
是个贴心的工具类,帮你快速组装 UserDetailsService
、AuthenticationProvider
等组件。
3. 为什么需要 AuthenticationManagerResolver?
传统的 AuthenticationManager
是静态的——整个应用用一个。但现实场景更复杂:比如员工和客户用不同的账号体系、不同的校验逻辑,甚至数据源都不同。
AuthenticationManagerResolver
就是为此而生的(Spring Security 5.2.0 引入):
public interface AuthenticationManagerResolver<C> {
AuthenticationManager resolve(C context);
}
它允许你根据请求上下文(context
)动态返回不同的 AuthenticationManager
。
✅ 关键点:Spring Security 已将该接口集成到认证流程中,支持使用 HttpServletRequest
或 ServerWebExchange
作为上下文,开箱即用。
4. 典型使用场景
设想一个系统有两类用户:
- 员工(访问
/employee/**
) - 客户(访问
/customer/**
)
他们:
- 使用不同的账号数据库
- 有不同的认证逻辑
- 只能访问自己对应的接口
这时,AuthenticationManagerResolver
就能派上大用场——根据请求路径选择对应的认证管理器。
5. AuthenticationManagerResolver 工作机制
你可以在任何需要动态选择 AuthenticationManager
的地方使用它,但本文聚焦于在 内置认证流程 中的应用。
我们将分别演示 Basic 和 OAuth2 场景下的集成方式。
5.1. 配置 AuthenticationManagerResolver
首先创建安全配置类:
@Configuration
public class CustomWebSecurityConfigurer {
// ...
}
为两类用户分别定义 AuthenticationManager
:
AuthenticationManager customersAuthenticationManager() {
return authentication -> {
if (isCustomer(authentication)) {
return new UsernamePasswordAuthenticationToken(/*credentials*/);
}
throw new UsernameNotFoundException(/*principal name*/);
};
}
员工的逻辑类似,只是判断条件不同:
public AuthenticationManager employeesAuthenticationManager() {
return authentication -> {
if (isEmployee(authentication)) {
return new UsernamePasswordAuthenticationToken(/*credentials*/);
}
throw new UsernameNotFoundException(/*principal name*/);
};
}
最后,定义 AuthenticationManagerResolver
,根据请求路径选择:
AuthenticationManagerResolver<HttpServletRequest> resolver() {
return request -> {
if (request.getPathInfo().startsWith("/employee")) {
return employeesAuthenticationManager();
}
return customersAuthenticationManager();
};
}
💡 这里用路径判断只是示例,你完全可以根据 Header、域名、租户 ID 等更复杂的逻辑来 resolve。
5.2. 用于 Basic 认证
Spring Security 5.2 引入了 AuthenticationFilter
,支持通过 AuthenticationManagerResolver
动态解析认证器。
配置步骤:
- 创建
AuthenticationFilter
,传入 resolver 和 converter:
private AuthenticationFilter authenticationFilter() {
AuthenticationFilter filter = new AuthenticationFilter(
resolver(), authenticationConverter());
filter.setSuccessHandler((request, response, auth) -> {});
return filter;
}
⚠️ 注意:这里设置了一个空的 SuccessHandler
,是为了避免认证成功后默认的重定向行为,适合 REST API 场景。
- 将 filter 加入安全链:
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.addFilterBefore(authenticationFilter(), BasicAuthenticationFilter.class);
return http.build();
}
这样,每当请求到来,AuthenticationFilter
会先提取认证信息,再通过 resolver 找到合适的 AuthenticationManager
执行认证。
5.3. 用于 OAuth2 认证
OAuth2 资源服务器使用 BearerTokenAuthenticationFilter
处理携带 Bearer Token
的请求。
我们可以通过 OAuth2ResourceServerConfigurer
注入 AuthenticationManagerResolver
:
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.oauth2ResourceServer()
.authenticationManagerResolver(resolver());
return http.build();
}
Spring 会自动将 resolver 与 BearerTokenAuthenticationFilter
集成,根据请求上下文选择合适的 AuthenticationManager
来校验 Token。
6. 响应式应用中的 AuthenticationManagerResolver
在 WebFlux 响应式编程中,对应的是 ReactiveAuthenticationManagerResolver
:
@FunctionalInterface
public interface ReactiveAuthenticationManagerResolver<C> {
Mono<ReactiveAuthenticationManager> resolve(C context);
}
它返回的是 Mono<ReactiveAuthenticationManager>
,适配响应式流。
6.1. 配置 ReactiveAuthenticationManagerResolver
安全配置类:
@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
public class CustomWebSecurityConfig {
// ...
}
定义响应式的认证管理器:
ReactiveAuthenticationManager customersAuthenticationManager() {
return authentication -> customer(authentication)
.switchIfEmpty(Mono.error(new UsernameNotFoundException(/*principal name*/)))
.map(b -> new UsernamePasswordAuthenticationToken(/*credentials*/));
}
员工的管理器类似:
public ReactiveAuthenticationManager employeesAuthenticationManager() {
return authentication -> employee(authentication)
.switchIfEmpty(Mono.error(new UsernameNotFoundException(/*principal name*/)))
.map(b -> new UsernamePasswordAuthenticationToken(/*credentials*/));
}
resolver 根据 ServerWebExchange
选择:
ReactiveAuthenticationManagerResolver<ServerWebExchange> resolver() {
return exchange -> {
if (match(exchange.getRequest(), "/employee")) {
return Mono.just(employeesAuthenticationManager());
}
return Mono.just(customersAuthenticationManager());
};
}
6.2. 用于 Basic 认证(响应式)
使用 AuthenticationWebFilter
替代阻塞式 filter:
@Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
return http
.csrf(csrfSpec -> csrfSpec.disable())
.authorizeExchange(auth -> auth.pathMatchers(HttpMethod.GET,"/**")
.authenticated())
.httpBasic(httpBasicSpec -> httpBasicSpec.disable())
.addFilterAfter(authenticationWebFilter(), SecurityWebFiltersOrder.REACTOR_CONTEXT)
.build();
}
⚠️ 关键点:**必须先禁用 .httpBasic()
**,否则会和自定义 filter 冲突。然后手动添加 AuthenticationWebFilter
并传入 resolver。
6.3. 用于 OAuth2 认证(响应式)
直接在 oauth2ResourceServer
中设置 resolver:
@Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
return http
// ...
.and()
.oauth2ResourceServer()
.authenticationManagerResolver(resolver())
.and()
// ...;
}
Spring 会自动创建并配置 AuthenticationWebFilter
,集成你的 resolver。
7. 总结
本文通过一个典型的多用户体系场景,演示了 AuthenticationManagerResolver
的核心价值:
- ✅ 支持 Basic 和 OAuth2 认证流程
- ✅ 可在 Servlet 和 Reactive 应用中使用
- ✅ 实现认证逻辑的动态分发,提升系统灵活性
📌 踩坑提醒:使用自定义
AuthenticationFilter
时,别忘了禁用默认的 Basic 或 OAuth2 配置,否则会冲突。
示例代码已托管至 GitHub: