| 
         对于最新的稳定版本,请使用 Spring Security 6.4.1!  | 
    
OAuth 2.0 资源服务器多租户
多租户
当有多种策略用于验证不记名令牌(由某个租户标识符作为键控)时,资源服务器被视为多租户。
例如,您的资源服务器可以接受来自两个不同授权服务器的持有者令牌。 或者,您的授权服务器可以表示多个颁发者。
在每种情况下,都需要做两件事,权衡取舍与您选择如何做这些事情有关:
- 
解析租户。
 - 
传播租户。
 
按声明解析租户
区分租户的一种方法是通过颁发者声明。由于颁发者声明随签名的 JWT 一起提供,因此您可以使用JwtIssuerReactiveAuthenticationManagerResolver:
- 
Java
 - 
Kotlin
 
JwtIssuerReactiveAuthenticationManagerResolver authenticationManagerResolver = JwtIssuerReactiveAuthenticationManagerResolver
    .fromTrustedIssuers("https://idp.example.org/issuerOne", "https://idp.example.org/issuerTwo");
http
    .authorizeExchange(exchanges -> exchanges
        .anyExchange().authenticated()
    )
    .oauth2ResourceServer(oauth2 -> oauth2
        .authenticationManagerResolver(authenticationManagerResolver)
    );
val customAuthenticationManagerResolver = JwtIssuerReactiveAuthenticationManagerResolver
    .fromTrustedIssuers("https://idp.example.org/issuerOne", "https://idp.example.org/issuerTwo")
return http {
    authorizeExchange {
        authorize(anyExchange, authenticated)
    }
    oauth2ResourceServer {
        authenticationManagerResolver = customAuthenticationManagerResolver
    }
}
这很好,因为颁发者端点是延迟加载的。
实际上,相应的JwtReactiveAuthenticationManager仅在发送具有相应颁发者的第一个请求时实例化。
这允许独立于这些授权服务器启动和可用的应用程序启动。
动态租户
您可能不希望在每次添加新租户时重新启动应用程序。
在这种情况下,您可以配置JwtIssuerReactiveAuthenticationManagerResolver使用存储库ReactiveAuthenticationManager实例,您可以在运行时对其进行编辑:
- 
Java
 - 
Kotlin
 
private Mono<ReactiveAuthenticationManager> addManager(
		Map<String, ReactiveAuthenticationManager> authenticationManagers, String issuer) {
	return Mono.fromCallable(() -> ReactiveJwtDecoders.fromIssuerLocation(issuer))
            .subscribeOn(Schedulers.boundedElastic())
            .map(JwtReactiveAuthenticationManager::new)
            .doOnNext(authenticationManager -> authenticationManagers.put(issuer, authenticationManager));
}
// ...
JwtIssuerReactiveAuthenticationManagerResolver authenticationManagerResolver =
        new JwtIssuerReactiveAuthenticationManagerResolver(authenticationManagers::get);
http
    .authorizeExchange(exchanges -> exchanges
        .anyExchange().authenticated()
    )
    .oauth2ResourceServer(oauth2 -> oauth2
        .authenticationManagerResolver(authenticationManagerResolver)
    );
private fun addManager(
        authenticationManagers: MutableMap<String, ReactiveAuthenticationManager>, issuer: String): Mono<JwtReactiveAuthenticationManager> {
    return Mono.fromCallable { ReactiveJwtDecoders.fromIssuerLocation(issuer) }
            .subscribeOn(Schedulers.boundedElastic())
            .map { jwtDecoder: ReactiveJwtDecoder -> JwtReactiveAuthenticationManager(jwtDecoder) }
            .doOnNext { authenticationManager: JwtReactiveAuthenticationManager -> authenticationManagers[issuer] = authenticationManager }
}
// ...
var customAuthenticationManagerResolver = JwtIssuerReactiveAuthenticationManagerResolver(authenticationManagers::get)
return http {
    authorizeExchange {
        authorize(anyExchange, authenticated)
    }
    oauth2ResourceServer {
        authenticationManagerResolver = customAuthenticationManagerResolver
    }
}
在这种情况下,您构造JwtIssuerReactiveAuthenticationManagerResolver使用获取ReactiveAuthenticationManager给发行人。
这种方法允许我们在存储库中添加和删除元素(显示为Map在前面的代码片段中)。
| 
 简单地采用任何发行者并构造一个  |