spring-cloud-config icon indicating copy to clipboard operation
spring-cloud-config copied to clipboard

Custom RestTemplate is not loaded

Open sydneyhenrard opened this issue 3 years ago • 3 comments

Describe the bug Spring Boot 2.4.5 Spring Cloud 2020.0.2

I deployed Spring Cloud Config Server on Google Cloud Run. It works fine when calling it from the command line (curl) with a generated access token using gcloud auth print-identity-token. I want to call it from a Spring Boot application with Spring Cloud Config Client. To retrieve the access token I customized the RestTemplate. The issue is that this customization is not loaded/called even though I followed the documentation https://docs.spring.io/spring-cloud-config/docs/3.0.0/reference/html/#_security_2. I checked by adding breakpoint in the custom configuration class but the methods are not called. I get an exception which is expected since the default RestTemplate is still in use:

	at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:602)
	at org.springframework.cloud.config.client.ConfigServerConfigDataLoader.getRemoteEnvironment(ConfigServerConfigDataLoader.java:269)
	at org.springframework.cloud.config.client.ConfigServerConfigDataLoader.doLoad(ConfigServerConfigDataLoader.java:105)

Sample

src/main/resources/application.yml

spring:
  config:
    import: configserver:https://xxxxxxx.run.app

src/main/resources/bootstrap.yml

spring:
  application:
    name: playground

src/main/resources/META-INF/spring.factories

org.springframework.cloud.bootstrap.BootstrapConfiguration=com.company.playground.CustomConfigServiceBootstrapConfiguration

The custom configuration

@Configuration
public class CustomConfigServiceBootstrapConfiguration {

    @Bean
    public ConfigServicePropertySourceLocator configServicePropertySourceLocator(ConfigClientProperties clientProperties) {
        ConfigServicePropertySourceLocator configServicePropertySourceLocator = new ConfigServicePropertySourceLocator(clientProperties);
        configServicePropertySourceLocator.setRestTemplate(restTemplate());
        return configServicePropertySourceLocator;
    }

    public RestTemplate restTemplate() {
        return new RestTemplateBuilder()
                .interceptors(new GCloudInterceptor())
                .build();
    }
}

The interceptor using google-auth-library-oauth2-http

public class GCloudInterceptor implements ClientHttpRequestInterceptor {
    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
        GoogleCredentials credentials = GoogleCredentials.getApplicationDefault();
        credentials.refreshIfExpired();
        AccessToken accessToken = credentials.getAccessToken();
        request.getHeaders().add(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken.getTokenValue());
        return execution.execute(request, body);
    }
}

sydneyhenrard avatar Apr 17 '21 13:04 sydneyhenrard

I managed to make it work but with some changes. Basically it's not possible to use the Spring Boot Config Data Import.

Set spring.cloud.bootstrap.enabled=true and then use the uri config in bootstrap.yml:

spring:
  application:
    name: playground
  cloud:
    config:
      uri: https://xxx.run.app

and remove spring.config.import in application.yml

Leaving the issue open because I don't know if it's a bug, or if it's the normal.

sydneyhenrard avatar Apr 17 '21 14:04 sydneyhenrard

See sample here https://github.com/spring-cloud/spring-cloud-config/blob/b1e795d8d191c04d1221ab88a30a6b2f431bba7f/spring-cloud-config-client/src/test/java/org/springframework/cloud/config/client/ConfigServerConfigDataCustomizationIntegrationTests.java#L59-L60

spencergibb avatar Jun 03 '21 17:06 spencergibb

@spencergibb It seems to me that the documentation doesn't write that if you want to use the CustomConfigServiceBootstrapConfiguration you must set the spring.cloud.bootstrap.enabled=true. It seems that Spring is going in the direction of putting everything in the application.yaml (one file) and in this case we go backward by using the old configuration.

Don't you think it should be fixed?

avnerstr avatar Jun 06 '21 16:06 avnerstr

@spencergibb Following our conversation in the slack channel, I found this open issue which is relevant to the problem I was describing. so maybe we can use this issue instead of creating a new one.

Here is my original message in the channel:

Spring cloud config-client docs has a section about providing custom RestTemplate which is useful in same cases like when the config-server is using oauth2 for security. It uses ConfigServicePropertySourceLocator as example for this customisation. But if i’m not mistaken this class (ConfigServicePropertySourceLocator) is only used in legacy bootstrap approach and the new/default way to fetch data from config-server is via ConfigServerConfigDataLoader . I think the docs should include an example for ConfigServerConfigDataLoader too and also it should point out that ConfigServicePropertySourceLocator would be useful only if legacy bootstrap approach is enabled.

kvmw avatar Jan 03 '23 17:01 kvmw

Do we have any update on this issue? This is the exact behavior/issue we are also getting what @kvmw had reported above and quoted from slack channel. Are we expecting any update in current ConfigServerConfigDataLoader class definition to have a support for Custom RestTemplate for use cases like oauth2; with the new/default way to fetch data from config-server?

abjimmypro avatar Feb 24 '23 04:02 abjimmypro

@abjimmypro The conversation was not about changing the code but only updating documentation.

You need to implement a BootstrapRegistryInitializer to register a RestTemplate with an auth interceptor. Here you can find a sample. You need to register above class in spring.factories. Check this sample again.

Please note that by the time BootstrapRegistryInitializer is called the application properties is not loaded yet. so you cannot load oauth2 configuration form properties file. You can use other approaches like environment variables ( System.getEnv(...) ) to access your configuration.

kvmw avatar Feb 24 '23 08:02 kvmw

@abjimmypro The conversation was not about changing the code but only updating documentation.

You need to implement a BootstrapRegistryInitializer to register a RestTemplate with an auth interceptor. Here you can find a sample. You need to register above class in spring.factories. Check this sample again.

Please note that by the time BootstrapRegistryInitializer is called the application properties is not loaded yet. so you cannot load oauth2 configuration form properties file. You can use other approaches like environment variables ( System.getEnv(...) ) to access your configuration.

Thanks @kvmw for clarification and sample.

abjimmypro avatar Feb 24 '23 20:02 abjimmypro