如何使用 client_credentials 从资源服务器访问另一个 oauth2 资源?
Posted
技术标签:
【中文标题】如何使用 client_credentials 从资源服务器访问另一个 oauth2 资源?【英文标题】:How can I use client_credentials to access another oauth2 resource from a resource server? 【发布时间】:2020-02-23 23:07:04 【问题描述】:我想使用 client_credentials 从响应式资源服务器访问另一个受 oauth2 保护的资源。我使用颁发的令牌访问资源服务器的部分正在工作,但没有使用 webclient 调用其他资源。
使用 UnAuthenticatedServerOAuth2AuthorizedClientRepository 我得到 serverWebExchange must be null
,使用 AuthenticatedPrincipalServerOAuth2AuthorizedClientRepository 我得到 principalName must be null
。
只要我将客户端称为CommandLineRunner
,就可以使用https://www.baeldung.com/spring-webclient-oauth2。我在 *** 上找到的其他建议都没有奏效。
我在这里缺少什么?我正在使用 Spring Security 5.2.0 和 Spring Boot 2.2.0。
客户端配置:
@Configuration
public class ClientSecurityConfig
// UnAuthenticatedServerOAuth2AuthorizedClientRepository version
@Bean
WebClient webClient(ReactiveClientRegistrationRepository clientRegistrations)
ServerOAuth2AuthorizedClientExchangeFilterFunction oauth =
new ServerOAuth2AuthorizedClientExchangeFilterFunction(clientRegistrations, new UnAuthenticatedServerOAuth2AuthorizedClientRepository());
return WebClient.builder()
.filter(oauth)
.build();
@Bean
ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider(CustomClientConfig clientConfig)
return ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
.clientCredentials(clientCredentialsGrantBuilder ->
clientCredentialsGrantBuilder.accessTokenResponseClient(new CustomClient(clientConfig))) // Used to send extra parameters to adfs server
.build();
// AuthenticatedPrincipalServerOAuth2AuthorizedClientRepository version
@Bean
WebClient webClient(ReactiveOAuth2AuthorizedClientManager authorizedClientManager)
ServerOAuth2AuthorizedClientExchangeFilterFunction oauth =
new ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
return WebClient.builder()
.filter(oauth)
.build();
@Bean
ReactiveOAuth2AuthorizedClientManager authorizedClientManager(
ReactiveClientRegistrationRepository clientRegistrationRepository,
ServerOAuth2AuthorizedClientRepository authorizedClientRepository, CustomClientConfig clientConfig)
ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
.clientCredentials(clientCredentialsGrantBuilder ->
clientCredentialsGrantBuilder.accessTokenResponseClient(new CustomClient(clientConfig))) // Used to send extra parameters to adfs server
.build();
DefaultReactiveOAuth2AuthorizedClientManager authorizedClientManager =
new DefaultReactiveOAuth2AuthorizedClientManager(clientRegistrationRepository, authorizedClientRepository);
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
return authorizedClientManager;
资源服务器配置:
@EnableWebFluxSecurity
class ResourceServerConfig
@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http)
http
.authorizeExchange(exchanges ->
exchanges
.pathMatchers("/actuators/**", "/api/v1").permitAll()
.pathMatchers("/api/v1/**").hasAuthority("SCOPE_read")
.anyExchange().authenticated()
)
.formLogin().disable()
.httpBasic().disable()
.oauth2Client(withDefaults())
.oauth2ResourceServer().jwt();
return http.build();
@RestController()
@RequestMapping("/api/v1")
static class Ctrl
final static Logger logger = LoggerFactory.getLogger(Ctrl.class);
final WebClient webClient;
public Ctrl(WebClient webClient)
this.webClient = webClient;
@RequestMapping("protected")
Mono<JsonNode> protected(@RequestParam String data)
return webClient.post()
.uri("https://other-oauth2-protected-resource")
.attributes(clientRegistrationId("myclient"))
.bodyValue("\"data\": \"" + data + "\"")
.retrieve()
.bodyToMono(JsonNode.class);
application.yml:
spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: http://adfsserver.com/adfs/services/trust
jwk-set-uri: https://adfsserver.com/adfs/discovery/keys
client:
registration:
myclient:
provider: adfs
client-id: <client-id>
client-secret: <client-secret>
authorization-grant-type: client_credentials
scope: read
provider:
adfs:
token-uri: https://adfsserver.com/adfs/oauth2/token
jwk-set-uri: https://adfsserver.com/adfs/discovery/keys
【问题讨论】:
似乎缺少 AuthorizedClientServiceOAuth2AuthorizedClientManager 的反应对应物;见github.com/spring-projects/spring-security/issues/7544 请检查我的回答(虽然我知道真的很晚了)。如果您接受并关闭问题,我们将不胜感激。 【参考方案1】:Spring 项目贡献者最近已将这个问题作为PR 的一部分进行了修复,但不幸的是,Spring 官方文档尚未更新。
普通的servlet方法文档是here 如果您更喜欢选择“反应式”方法,那么配置 webclient 只需要两个 bean:
-
AuthorizedClientManager Bean,以及
webClient Bean
@Bean
public ReactiveOAuth2AuthorizedClientManager authorizedClientManager(
ReactiveClientRegistrationRepository clientRegistrationRepository,
ReactiveOAuth2AuthorizedClientService authorizedClientService)
ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
.clientCredentials()
.build();
AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager authorizedClientManager =
new AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager(
clientRegistrationRepository, authorizedClientService);
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
return authorizedClientManager;
@Bean
public WebClient webClient(ReactiveOAuth2AuthorizedClientManager authorizedClientManager)
ServerOAuth2AuthorizedClientExchangeFilterFunction oauth = new ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
return WebClient.builder().filter(oauth).build();
你可以参考我的Github Gist,里面有所有需要的配置。
【讨论】:
以上是关于如何使用 client_credentials 从资源服务器访问另一个 oauth2 资源?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 Spring Security 为 client_credentials 工作流向 Feign 客户端提供 OAuth2 令牌
OneLogin是否支持client_credentials?
带有 client_credentials 的 Spring Oauth2 不验证用户
spring security client_credentials grant_type - 支持刷新令牌
spring-oauth-server入门(1-11)使用授权方式四:client_credentials 模式的客戶端