# OAuth2.0 资源服务器多租户

## 多重租赁

当存在多个用于验证由某个租户标识符控制的承载令牌的策略时，资源服务器被认为是多租户的。

例如，你的资源服务器可能接受来自两个不同授权服务器的承载令牌。或者，你的授权服务器可能表示多个发行者。

在每种情况下，都需要做两件事，以及与选择如何做这些事情相关的权衡：

1. 解决租户

2. 宣传租户

### 通过索赔来解决承租人的问题

区分租户的一种方法是通过签发人索赔。由于发行者声明伴随着已签名的 JWTS，因此可以使用`JwtIssuerReactiveAuthenticationManagerResolver`来完成此操作，如下所示：

爪哇

```
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)
    );
```

Kotlin

```
val customAuthenticationManagerResolver = JwtIssuerReactiveAuthenticationManagerResolver("https://idp.example.org/issuerOne", "https://idp.example.org/issuerTwo")

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

这很好，因为发行者端点是懒洋洋地加载的。实际上，对应的`JwtReactiveAuthenticationManager`只有在发送了与对应的发行者的第一个请求时才被实例化。这允许应用程序启动，该应用程序独立于已启动和可用的那些授权服务器。

#### 动态租户

当然，你可能不希望每次添加新租户时都重新启动应用程序。在这种情况下，你可以使用`JwtIssuerReactiveAuthenticationManagerResolver`实例的存储库配置`ReactiveAuthenticationManager`实例，你可以在运行时对其进行编辑，如下所示：

爪哇

```
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)
    );
```

Kotlin

```
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`）。

|   |简单地获取任何发行者并从中构造`ReactiveAuthenticationManager`将是不安全的。<br/>发行者应该是代码可以从可信来源（如允许的发行者列表）验证的发行者。|
|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

[不透明令牌](opaque-token.html)[不记名代币](bearer-tokens.html)

