Spring 对不同 OAuth 服务器的安全多次调用需要 SecurityContext 中的多个令牌
Spring Security multiple calls to different OAuth servers requiring multiple tokens in SecurityContext
我有一个 spring 应用程序可以验证其余端点上的 JWT 令牌。
使用SecurityChain
.oAuth2ResourceServer()
.jwt()
这似乎在 ReactiveSecurityContextHolder
中创建了一个 JwtAuthenticationToken
。
然后我想从这个端点流出输入,客户端通过检查不记名令牌进行身份验证。然后使用 webClient
调用另一个休息服务。此 Web 客户端需要使用不同的 OAuth 服务器使用外部服务使用授权类型密码进行身份验证,并获取自己的不记名令牌。
问题是 Web 客户端使用了包含经过身份验证的 JWT 的 ReactiveSecurityContextHolder
。并尝试使用此信息而不是将我的应用程序连接到其余端点并对其进行身份验证。
我已经设置了 Yaml 来注册我的客户端
spring:
security:
oauth2:
client:
registration:
Myapp:
client-id:
client-secret:
token-uri:
authorization-grant-type:
然后添加过滤功能
ServerOAuth2AuthorizedClientExchangeFilterFunction
但我得到了 principalName cannot be empty
,因为它似乎在我的应用程序的其余端点上通过验证调用者来重用安全上下文。
应该如何设计或提供示例来展示如何使用不同的安全上下文或在服务到服务调用之间以不同方式获取令牌?
你说得对,ServerOAuth2AuthorizedClientExchangeFilterFunction
的设计是基于 currently-authorized 客户端,你已经解释过你不想在这种情况下使用它。
您已表示要使用客户端的凭据作为资源所有者密码授予的用户名和密码。但是,Spring 安全性中没有任何内容可以做到这一点。
但是,您可以直接使用 WebClientReactivePasswordTokenResponseClient
以便自己制定自定义请求。
简而言之,这将是一个自定义 ExchangeFilterFunction
,看起来像:
ClientRegistrationRespository clientRegistrations;
ReactiveOAuth2AccessTokenResponseClient<OAuth2PasswordGrantRequest>
accessTokenResponseClient = new WebClientReactivePasswordTokenResponseClient();
Mono<ClientResponse> filter(ClientRequest request, ExchangeFunction next) {
return this.clientRegistrations.findByRegistrationId("registration-id")
.map(clientRegistration -> new OAuth2PasswordGrantRequest(
clientRegistration,
clientRegistration.getClientId(),
clientRegistration.getClientSecret())
.map(this.accessTokenResponseClient::getTokenResponse)
.map(tokenResponse -> ClientRequest.from(request)
.headers(h -> h.setBearerAuth(tokenResponse.getAccessToken().getTokenValue())
.build())
.flatMap(next::exchange);
}
(为简洁起见,我删除了所有错误处理。)
以上代码经过以下步骤:
- 查找适当的客户端注册 -- 这包含提供商的端点以及客户端 ID 和密码
- 构造一个
OAuth2PasswordGrantRequest
,使用client的id和secret作为资源所有者的用户名和密码
- 使用
WebClientReactivePasswordTokenResponseClient
执行请求
- 将访问令牌设置为请求的承载令牌
- 继续链中的下一个函数
请注意,要使用 Spring Security 的 OAuth 2.0 客户端功能,您还需要将您的应用配置为客户端。这意味着除了 .oauth2ResourceServer()
之外,至少要更改您的 DSL 以包含 .oauth2Client()
。它还将意味着配置 ClientRegistrationRepository
。为了让我的评论集中在过滤器功能上,我省略了这个细节,但如果有必要,我也很乐意提供帮助。
我有一个 spring 应用程序可以验证其余端点上的 JWT 令牌。
使用SecurityChain
.oAuth2ResourceServer()
.jwt()
这似乎在 ReactiveSecurityContextHolder
中创建了一个 JwtAuthenticationToken
。
然后我想从这个端点流出输入,客户端通过检查不记名令牌进行身份验证。然后使用 webClient
调用另一个休息服务。此 Web 客户端需要使用不同的 OAuth 服务器使用外部服务使用授权类型密码进行身份验证,并获取自己的不记名令牌。
问题是 Web 客户端使用了包含经过身份验证的 JWT 的 ReactiveSecurityContextHolder
。并尝试使用此信息而不是将我的应用程序连接到其余端点并对其进行身份验证。
我已经设置了 Yaml 来注册我的客户端
spring:
security:
oauth2:
client:
registration:
Myapp:
client-id:
client-secret:
token-uri:
authorization-grant-type:
然后添加过滤功能
ServerOAuth2AuthorizedClientExchangeFilterFunction
但我得到了 principalName cannot be empty
,因为它似乎在我的应用程序的其余端点上通过验证调用者来重用安全上下文。
应该如何设计或提供示例来展示如何使用不同的安全上下文或在服务到服务调用之间以不同方式获取令牌?
你说得对,ServerOAuth2AuthorizedClientExchangeFilterFunction
的设计是基于 currently-authorized 客户端,你已经解释过你不想在这种情况下使用它。
您已表示要使用客户端的凭据作为资源所有者密码授予的用户名和密码。但是,Spring 安全性中没有任何内容可以做到这一点。
但是,您可以直接使用 WebClientReactivePasswordTokenResponseClient
以便自己制定自定义请求。
简而言之,这将是一个自定义 ExchangeFilterFunction
,看起来像:
ClientRegistrationRespository clientRegistrations;
ReactiveOAuth2AccessTokenResponseClient<OAuth2PasswordGrantRequest>
accessTokenResponseClient = new WebClientReactivePasswordTokenResponseClient();
Mono<ClientResponse> filter(ClientRequest request, ExchangeFunction next) {
return this.clientRegistrations.findByRegistrationId("registration-id")
.map(clientRegistration -> new OAuth2PasswordGrantRequest(
clientRegistration,
clientRegistration.getClientId(),
clientRegistration.getClientSecret())
.map(this.accessTokenResponseClient::getTokenResponse)
.map(tokenResponse -> ClientRequest.from(request)
.headers(h -> h.setBearerAuth(tokenResponse.getAccessToken().getTokenValue())
.build())
.flatMap(next::exchange);
}
(为简洁起见,我删除了所有错误处理。)
以上代码经过以下步骤:
- 查找适当的客户端注册 -- 这包含提供商的端点以及客户端 ID 和密码
- 构造一个
OAuth2PasswordGrantRequest
,使用client的id和secret作为资源所有者的用户名和密码 - 使用
WebClientReactivePasswordTokenResponseClient
执行请求
- 将访问令牌设置为请求的承载令牌
- 继续链中的下一个函数
请注意,要使用 Spring Security 的 OAuth 2.0 客户端功能,您还需要将您的应用配置为客户端。这意味着除了 .oauth2ResourceServer()
之外,至少要更改您的 DSL 以包含 .oauth2Client()
。它还将意味着配置 ClientRegistrationRepository
。为了让我的评论集中在过滤器功能上,我省略了这个细节,但如果有必要,我也很乐意提供帮助。