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 是个贴心的工具类,帮你快速组装 UserDetailsServiceAuthenticationProvider 等组件。

3. 为什么需要 AuthenticationManagerResolver?

传统的 AuthenticationManager 是静态的——整个应用用一个。但现实场景更复杂:比如员工和客户用不同的账号体系、不同的校验逻辑,甚至数据源都不同。

AuthenticationManagerResolver 就是为此而生的(Spring Security 5.2.0 引入):

public interface AuthenticationManagerResolver<C> {
    AuthenticationManager resolve(C context);
}

它允许你根据请求上下文(context)动态返回不同的 AuthenticationManager

关键点:Spring Security 已将该接口集成到认证流程中,支持使用 HttpServletRequestServerWebExchange 作为上下文,开箱即用。

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 动态解析认证器。

配置步骤:

  1. 创建 AuthenticationFilter,传入 resolver 和 converter:
private AuthenticationFilter authenticationFilter() {
    AuthenticationFilter filter = new AuthenticationFilter(
      resolver(), authenticationConverter());
    filter.setSuccessHandler((request, response, auth) -> {});
    return filter;
}

⚠️ 注意:这里设置了一个空的 SuccessHandler是为了避免认证成功后默认的重定向行为,适合 REST API 场景。

  1. 将 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 的核心价值:

  • ✅ 支持 BasicOAuth2 认证流程
  • ✅ 可在 ServletReactive 应用中使用
  • ✅ 实现认证逻辑的动态分发,提升系统灵活性

📌 踩坑提醒:使用自定义 AuthenticationFilter 时,别忘了禁用默认的 Basic 或 OAuth2 配置,否则会冲突。

示例代码已托管至 GitHub:


原始标题:Guide to the AuthenticationManagerResolver in Spring Security | Baeldung