对于最新的稳定版本,请使用 Spring Security 6.5.3spring-doc.cadn.net.cn

OAuth 2.0 资源服务器多租户

多租户

当有多种策略用于验证持有者Tokens(由某些租户标识符键)时,资源服务器被视为多租户。spring-doc.cadn.net.cn

例如,资源服务器可能接受来自两个不同授权服务器的持有者Tokens。 或者,您的授权服务器可能代表多个颁发者。spring-doc.cadn.net.cn

在每种情况下,都需要做两件事,以及与您选择如何做这些事情相关的权衡:spring-doc.cadn.net.cn

  1. 解决租户问题spring-doc.cadn.net.cn

  2. 传播租户spring-doc.cadn.net.cn

通过索赔解决租户问题

区分租户的一种方法是通过发行人索赔。由于发行人声明伴随着已签署的 JWT,因此可以使用JwtIssuerReactiveAuthenticationManagerResolver这样:spring-doc.cadn.net.cn

JwtIssuerReactiveAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerReactiveAuthenticationManagerResolver
    ("https://idp.example.org/issuerOne", "https://idp.example.org/issuerTwo");

http
    .authorizeExchange(exchanges -> exchanges
        .anyExchange().authenticated()
    )
    .oauth2ResourceServer(oauth2 -> oauth2
        .authenticationManagerResolver(authenticationManagerResolver)
    );
val customAuthenticationManagerResolver = JwtIssuerReactiveAuthenticationManagerResolver("https://idp.example.org/issuerOne", "https://idp.example.org/issuerTwo")

return http {
    authorizeExchange {
        authorize(anyExchange, authenticated)
    }
    oauth2ResourceServer {
        authenticationManagerResolver = customAuthenticationManagerResolver
    }
}

这很好,因为颁发者端点是延迟加载的。 事实上,相应的JwtReactiveAuthenticationManager仅在发送具有相应颁发者的第一个请求时实例化。 这允许独立于那些已启动且可用的授权服务器的应用程序启动。spring-doc.cadn.net.cn

动态租户

当然,您可能不希望在每次添加新租户时重新启动应用程序。 在这种情况下,您可以配置JwtIssuerReactiveAuthenticationManagerResolver存储库为ReactiveAuthenticationManager实例,您可以在运行时编辑这些实例,如下所示:spring-doc.cadn.net.cn

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在片段中)运行时。spring-doc.cadn.net.cn

简单地采用任何发行人并构建一个ReactiveAuthenticationManager从中。 颁发者应该是代码可以从受信任的源(例如允许的颁发者列表)验证的颁发者。