I'm currently trying to write a Spring Boot starter that would automatically authenticate our microservices with an API gateway and include the access token in the headers for all outgoing requests (towards the gateway).
I'm creating a RestTemplate bean and giving it our custom interceptor, but my problem is, by doing this, I prevent other teams (who would be using this starter) to use their own RestTemplate config, since they would have to define the same bean leading to multiple beans existing.
@Bean
public RestTemplate restTemplate(){
RestTemplate restTemplate = new RestTemplate();
List<ClientHttpRequestInterceptor> interceptors = restTemplate.getInterceptors();
if (interceptors.isEmpty()){
interceptors = new ArrayList<>();
}
interceptors.add(clientCredentialsAuthInterceptor());
restTemplate.setInterceptors(interceptors);
return restTemplate;
}
Is there another way of intercepting all outgoing requests or make the RestTemplate further configurable?
Not tested but it may give you a start point:
// Create an interface that users of your dependency
// can implement which provides interceptors
public interface RestTemplateAuthInterceptorProvider {
// This interface provides interceptors, so they can add as many as they want
List<ClientHttpRequestInterceptor> provideInterceptor();
}
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
// define a conditional default implementation of your interceptor provider
@Bean
@ConditionalOnMissingBean(RestTemplateAuthInterceptorProvider.class)
public RestTemplateAuthInterceptorProvider restTemplateAuthInterceptorProvider() {
return () -> ... // implement your auth interceptor and return
}
// In your actual rest template creation use method argument injection
// If other teams implement the RestTemplateAuthInterceptorProvider interface
// conditional case above will be false and your implementation will not interfere
// If they dont implement RestTemplateAuthInterceptorProvider
// your default implementation will be here
@Bean
public RestTemplate restTemplate(RestTemplateAuthInterceptorProvider provider) {
RestTemplate restTemplate = new RestTemplate();
List<ClientHttpRequestInterceptor> interceptors = restTemplate.getInterceptors();
if (interceptors == null){
interceptors = new ArrayList<>();
}
interceptors.addAll(provider.provideInterceptor()); // from argument
restTemplate.setInterceptors(interceptors);
return restTemplate;
}
Edit: Another hacky approach is to manipulate already defined RestTemplate beans
@Component
@ConditionalOnBean(RestTemplate.class)
public class RestTemplateBeanCustomizer {
private List<RestTemplate> restTemplateBeans;
// This injects all restTemplate bean definitions to your bean as a list
@Autowired
public RestTemplateBeanCustomizer(List<RestTemplate> restTemplateBeans) {
this.restTemplateBeans = restTemplateBeans;
}
@PostConstruct
public void customizeRestTemplateBeans() {
for (RestTemplate restTemplate : restTemplateBeans) {
// Add your interceptors message handlers etc
// restTemplate.set...
}
}
}