OAuth 2.0 Resource Server Multi-tenancy
Multi-tenancy
当验证 bearer token 有多种策略(以某种租户标识符为键)时,资源服务器被认为是多租户。
A resource server is considered multi-tenant when there are multiple strategies for verifying a bearer token, keyed by some tenant identifier.
例如,您的资源服务器可以接受来自两个不同授权服务器的持有者令牌。或者,您的授权服务器可以代表多个发行者。
For example, your resource server can accept bearer tokens from two different authorization servers. Alternately, your authorization server can represent a multiplicity of issuers.
在每种情况下,都需要做两件事,并且如何选择做这两件事存在权衡利弊:
In each case, two things need to be done and trade-offs are associated with how you choose to do them:
-
Resolve the tenant.
-
Propagate the tenant.
Resolving the Tenant By Claim
一种区分租户的方法是通过发行者声明。由于发行者声明附带已签名的 JWT,因此您可以使用 JwtIssuerReactiveAuthenticationManagerResolver
执行此操作:
One way to differentiate tenants is by the issuer claim. Since the issuer claim accompanies signed JWTs, you can do so with the 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
仅在发送了具有相应发行者的第一个请求时才实例化。这允许应用程序启动独立于这些授权服务器是否启动和可用。
This is nice because the issuer endpoints are loaded lazily.
In fact, the corresponding JwtReactiveAuthenticationManager
is instantiated only when the first request with the corresponding issuer is sent.
This allows for an application startup that is independent from those authorization servers being up and available.
Dynamic Tenants
您可能不想在每次添加新租户时都重新启动应用程序。在这种情况下,您可以使用 ReactiveAuthenticationManager
实例存储库配置 JwtIssuerReactiveAuthenticationManagerResolver
,您可以在运行时对其进行编辑:
You may not want to restart the application each time a new tenant is added.
In this case, you can configure the JwtIssuerReactiveAuthenticationManagerResolver
with a repository of ReactiveAuthenticationManager
instances, which you can edit at runtime:
-
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
}
}
在这种情况下,您使用获取 ReactiveAuthenticationManager
并传递给发行者给定的策略来构建 JwtIssuerReactiveAuthenticationManagerResolver
。此方法允许我们在运行时添加和移除存储库元素(在先前代码段中显示为 Map
)。
In this case, you construct JwtIssuerReactiveAuthenticationManagerResolver
with a strategy for obtaining the ReactiveAuthenticationManager
given to the issuer.
This approach lets us add and remove elements from the repository (shown as a Map
in the preceding snippet) at runtime.
简单地使用任何发行者并从中构建 It would be unsafe to simply take any issuer and construct an |