如何使用client_credentials从资源服务器访问另一个oauth2资源?

时间:2019-10-28 16:47:08

标签: spring-boot spring-security spring-security-oauth2 spring-webflux

我想使用client_credentials从反应式资源服务器访问另一个受oauth2保护的资源。我使用发出的令牌访问资源服务器的部分正在工作,但没有使用webclient调用其他资源。

使用UnAuthenticatedServerOAuth2AuthorizedClientRepository我得到serverWebExchange must be null,并使用AuthenticatedPrincipalServerOAuth2AuthorizedClientRepository我得到principalName must be null

只要我将客户端称为CommandLineRunner,就可以使用https://www.baeldung.com/spring-webclient-oauth2。我在stackoverflow上找到的其他建议都没有奏效。

我在这里想念什么?我正在使用Spring Security 5.2.0和Spring Boot 2.2.0。

ClientConfig:

@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;
    }
}

ResourceServerConfig:

@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

1 个答案:

答案 0 :(得分:4)

Spring项目贡献者最近已将其修复为PR的一部分,但很遗憾,官方的Spring文档尚未更新。

常规servlet方法文档为here 如果您希望选择“反应式”方法,则配置Web客户端仅需要两个bean:

  1. AuthorizedClientManager Bean和
  2. 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,其中具有所有必需的配置。