Spring Security 身份驗證管理器解析器指南

Spring Security
Remote
1
06:44 AM · Nov 30 ,2025

1. 簡介

在本教程中,我們將介紹 AuthenticationManagerResolver,並演示如何使用它進行 Basic 和 OAuth2 身份驗證流程。

2. 什麼是 AuthenticationManager ?

簡單來説,AuthenticationManager 是認證的主要策略接口。

如果輸入認證的 principal 有效且已驗證,AuthenticationManager#authenticate 將返回一個 Authentication 實例,其中 authenticated 標誌設置為 true。 否則,如果 principal 無效,它將拋出 AuthenticationException。 對於最後一種情況,如果無法確定,它將返回 null

ProviderManagerAuthenticationManager 的默認實現。 它將認證過程委託給一個 AuthenticationProvider 實例列表。

當創建 SecurityFilterChain bean 時,我們可以設置全局或本地 AuthenticationManager。 對於本地 AuthenticationManager,我們可以創建一個 AuthenticationManager bean,通過 HttpSecurity 訪問 AuthenticationManagerBuilder

AuthenticationManagerBuilder 是一個輔助類,可以簡化 UserDetailServiceAuthenticationProvider 以及其他依賴項的設置,從而構建 AuthenticationManager

對於全局 AuthenticationManager,我們應該將 AuthenticationManager 定義為一個 bean。

3. 為什麼使用 AuthenticationManagerResolver?

AuthenticationManagerResolver 允許 Spring 根據上下文選擇特定的 AuthenticationManager。這是一個在 Spring Security 5.2.0 版本中新增的功能:

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

AuthenticationManagerResolver#resolve 可以根據泛型上下文返回一個 AuthenticationManager 實例。換句話説,我們可以將一個類設置為上下文,以便根據它來解決 AuthenticationManager

Spring Security 已將 AuthenticationManagerResolver 集成到認證流程中,使用 HttpServletRequestServerWebExchange 作為上下文。

4. 場景使用

讓我們看看如何在實踐中運用 AuthenticationManagerResolver

例如,假設一個系統擁有兩個用户組:員工和客户。這兩個用户組具有特定的身份驗證邏輯,並且擁有獨立的數據庫。此外,這兩個用户組中的用户只能調用其相關的 URL。

5. 如何工作 AuthenticationManagerResolver

我們可以使用 AuthenticationManagerResolver,只要我們需要動態地選擇 AuthenticationManager,但在此教程中,我們對使用它在內置身份驗證流程中感興趣。

首先,讓我們設置一個 AuthenticationManagerResolver,然後使用它進行基本和 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*/);
    };
}

員工的 AuthenticationManager 在邏輯上與員工相同,我們只是用 AuthenticationManagerResolver 替代 AuthenticationManagerResolver

public AuthenticationManager employeesAuthenticationManager() {
    return authentication -> {
        if (isEmployee(authentication)) {
            return new UsernamePasswordAuthenticationToken(/*credentials*/);
        }
        throw new UsernameNotFoundException(/*principal name*/);
    };
}

最後,讓我們添加一個 AuthenticationManagerResolver,它根據請求的 URL 進行解析:

AuthenticationManagerResolver<HttpServletRequest> resolver() {
    return request -> {
        if (request.getPathInfo().startsWith("/employee")) {
            return employeesAuthenticationManager();
        }
        return customersAuthenticationManager();
    };
}

5.2. 用於基本身份驗證

我們可以使用 AuthenticationFilter 動態地解決 AuthenticationManagerAuthenticationFilter 在 Spring Security 5.2 中添加。

如果我們將其添加到安全過濾器鏈中,那麼對於匹配的請求,它首先檢查是否可以提取任何身份驗證對象,如果存在,則它會向 AuthenticationManagerResolver 請求合適的 AuthenticationManager 並繼續流程。

首先,讓我們在 CustomWebSecurityConfigurer 中創建一個方法來創建 AuthenticationFilter

private AuthenticationFilter authenticationFilter() {
    AuthenticationFilter filter = new AuthenticationFilter(
      resolver(), authenticationConverter());
    filter.setSuccessHandler((request, response, auth) -> {});
    return filter;
}

原因是將 AuthenticationFilterSuccessHandler 設置為無操作,以防止在成功身份驗證後重定向的默認行為。

然後,我們可以通過在 CustomWebSecurityConfigurer 中創建一個 SecurityFilterChain bean,將此過濾器添加到安全過濾器鏈中:

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http.addFilterBefore(authenticationFilter(), BasicAuthenticationFilter.class);
    return http.build();
}

5.3. 用於 OAuth2 身份驗證

BearerTokenAuthenticationFilter 負責 OAuth2 身份驗證。 doFilterInternal 方法檢查請求中是否存在 BearerTokenAuthenticationToken,如果存在,則它會解析適當的 AuthenticationManager 來驗證令牌。

OAuth2ResourceServerConfigurer 用於設置 BearerTokenAuthenticationFilter

因此,我們可以通過在 CustomWebSecurityConfigurer 中創建一個 SecurityFilterChain bean,為資源服務器設置 AuthenticationManagerResolver

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http
      .oauth2ResourceServer()
      .authenticationManagerResolver(resolver());
    return http.build();
}

6. Resolve AuthenticationManager in Reactive Applications

對於一個反應式 Web 應用程序,我們仍然可以從上下文層面解決 AuthenticationManager 的概念。但是這裏我們有 ReactiveAuthenticationManagerResolver 代替:

@FunctionalInterface
public interface ReactiveAuthenticationManagerResolver<C> {
    Mono<ReactiveAuthenticationManager> resolve(C context);
}

它返回一個 MonoReactiveAuthenticationManagerReactiveAuthenticationManagerAuthenticationManager 的反應式等價物,因此它的 authenticate 方法返回 Mono.

6.1. Setting Up ReactiveAuthenticationManagerResolver

讓我們首先創建一個用於安全配置的類:

@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
public class CustomWebSecurityConfig {
    // ...
}

接下來,讓我們在類中定義 ReactiveAuthenticationManager 用於客户:

ReactiveAuthenticationManager customersAuthenticationManager() {
    return authentication -> customer(authentication)
      .switchIfEmpty(Mono.error(new UsernameNotFoundException(/*principal name*/)))
      .map(b -> new UsernamePasswordAuthenticationToken(/*credentials*/));
}

然後,我們定義用於員工的 ReactiveAuthenticationManager:

public ReactiveAuthenticationManager employeesAuthenticationManager() {
    return authentication -> employee(authentication)
      .switchIfEmpty(Mono.error(new UsernameNotFoundException(/*principal name*/)))
      .map(b -> new UsernamePasswordAuthenticationToken(/*credentials*/));
}

最後,我們設置基於場景的 ReactiveAuthenticationManagerResolver:

ReactiveAuthenticationManagerResolver<ServerWebExchange> resolver() {
    return exchange -> {
        if (match(exchange.getRequest(), "/employee")) {
            return Mono.just(employeesAuthenticationManager());
        }
        return Mono.just(customersAuthenticationManager());
    };
}

6.2. For Basic Authentication

在反應式 Web 應用程序中,我們可以使用 AuthenticationWebFilter 進行身份驗證。它驗證請求並填充安全上下文。

AuthenticationWebFilter 首先檢查請求是否匹配。之後,如果請求中存在身份驗證對象,它會從 ReactiveAuthenticationManagerResolver 中獲取合適的 ReactiveAuthenticationManager 並繼續身份驗證流程。

因此,我們可以將自定義的 AuthenticationWebFilter 設置到我們的安全配置中:

@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();
}

首先,我們禁用 ServerHttpSecurity#httpBasic 以防止正常的身份驗證流程,然後手動替換它為 AuthenticationWebFilter,並傳入我們的自定義解析器。

6.3. For OAuth2 Authentication

我們可以使用 ServerHttpSecurity#oauth2ResourceServer 配置 ReactiveAuthenticationManagerResolverServerHttpSecurity#build 將一個 AuthenticationWebFilter 實例,該實例包含我們的解析器,添加到過濾器的鏈中。

因此,我們可以在我們的安全配置中定義用於 OAuth2 身份驗證的 AuthenticationManagerResolver:

@Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
    return http
      // ...
      .and()
      .oauth2ResourceServer()
      .authenticationManagerResolver(resolver())
      .and()
      // ...;
}

7. 結論

在本文中,我們使用了 AuthenticationManagerResolver 在一個簡單的場景中用於 Basic 和 OAuth2 認證。

此外,我們還探索了 ReactiveAuthenticationManagerResolver 在反應式 Spring Web 應用程序中用於 Basic 和 OAuth2 認證。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.