Exposing OAuth2RestTemplate as AsyncRestTemplate - spring

I have a working OAuth2RestTemplate client (I'm using spring-security-oauth2 2.0.7.RELEASE). Now I'd like to expose/wrap it as AsyncRestTemplate to take advantage of asynchronous semantic of ListenableFuture. Unfortunately the following straightforward approach doesn't work:
// instantiate and configure OAuth2RestTemplate - works
OAuth2RestTemplate oAuth2RestTemplate = new OAuth2RestTemplate(...);
// wrap sync restTemplate with AsyncRestTemplate - doesn't work
AsyncRestTemplate asyncRestTemplate = new AsyncRestTemplate(
new HttpComponentsAsyncClientHttpRequestFactory(), oAuth2RestTemplate);
How can I get OAuth2 Rest client for my HTTP service as AsyncRestTemplate?

Ok, I was able to make AsyncRestTemplate work by manually setting "Authorization" header with accessToken from OAuth2RestTemplate; here's Spring java configuration for that:
#Bean
public OAuth2RestTemplate restTemplate() {
ClientCredentialsResourceDetails details = new ClientCredentialsResourceDetails();
// configure oauth details
OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(details);
restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory());
return restTemplate;
}
#Bean
public AsyncRestTemplate asyncRestTemplate(final OAuth2RestTemplate oAuth2RestTemplate) {
HttpComponentsAsyncClientHttpRequestFactory asyncRequestFactory = new HttpComponentsAsyncClientHttpRequestFactory() {
#Override
public AsyncClientHttpRequest createAsyncRequest(URI uri, HttpMethod httpMethod) throws IOException {
AsyncClientHttpRequest asyncRequest = super.createAsyncRequest(uri, httpMethod);
OAuth2AccessToken accessToken = oAuth2RestTemplate.getAccessToken();
asyncRequest.getHeaders().set("Authorization", String.format("%s %s", accessToken.getTokenType(), accessToken.getValue()));
return asyncRequest;
}
};
return new AsyncRestTemplate(asyncRequestFactory, oAuth2RestTemplate);
}
I wish there would be easier way to expose configured OAuth2RestTemplate as AsyncRestTemplate in Spring.

The above works, but I've found a much neater way of doing it.
Register an implementation AsyncClientHttpRequestInterceptor
Example code:
private class Oauth2RequestInterceptor implements AsyncClientHttpRequestInterceptor
{
private final OAuth2RestTemplate oAuth2RestTemplate;
public Oauth2RequestInterceptor( OAuth2RestTemplate oAuth2RestTemplate )
{
this.oAuth2RestTemplate = oAuth2RestTemplate;
}
public ListenableFuture<ClientHttpResponse> intercept( HttpRequest request, byte[] body,
AsyncClientHttpRequestExecution execution ) throws IOException
{
OAuth2AccessToken accessToken = oAuth2RestTemplate.getAccessToken();
request.getHeaders()
.set( "Authorization", String.format( "%s %s", accessToken.getTokenType(), accessToken.getValue() ) );
return execution.executeAsync( request, body );
}
}
Then register it with your AsyncRestTemplate:
#Bean
public AsyncRestTemplate asyncRestTemplate( AsyncClientHttpRequestFactory factory, OAuth2RestTemplate restTemplate )
{
AsyncRestTemplate asyncRestTemplate = new AsyncRestTemplate( factory, restTemplate );
asyncRestTemplate.setInterceptors( Collections.singletonList( new Oauth2RequestInterceptor( restTemplate ) ) );
return asyncRestTemplate;
}

Related

Spring boot - corrupt authorization header value after RestTemplate.exchange()

I'm having a weird problem, i'm using tokens on Microservice enviroment, I need to call another service with the token already generated.
Call to other service on a Client class... There is the method.
return restTemplate.exchange(URL_REST_METHOD,
HttpMethod.GET,
httpEntity, //HEADER OK
SubscriptionDto.class,
subscriptionId);
To do things easy, I get the previous request, get the token and added to current request.
public class HeaderLoginInterceptor implements ClientHttpRequestInterceptor {
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
HttpServletRequest previousRequest = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
if(previousRequest.getHeader(AUTHORIZATION) != null){
request.getHeaders().add(AUTHORIZATION, previousRequest.getHeader(AUTHORIZATION));
}
return execution.execute(request, body);
}
}
I attach this interceptor to the Bean RestTemplate
#Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
List<ClientHttpRequestInterceptor> interceptors
= restTemplate.getInterceptors();
if (CollectionUtils.isEmpty(interceptors)) {
interceptors = new ArrayList<>();
}
interceptors.add(new HeaderLoginInterceptor());
restTemplate.setInterceptors(interceptors);
return restTemplate;
}
The weird thing, after the execution, I see a defect on Authorization header:
Correct one:
Bearer  eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJhdWQiOiJBZGlkYXMgQ2hhbGxlbmdlIiwic3...
Weird after:
Bearer  eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJhdWQiOiJBZGlkYXMgQ2hhbGxlbmdlIiwic3...
I try adding the token directly before call exchange method, same result.
HttpServletRequest previousRequest = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
HttpHeaders headers = new HttpHeaders();
headers.addAll((MultiValueMap<String, String>) previousRequest.getHeaderNames());
HttpEntity<?> httpEntity = new HttpEntity<>(new HttpHeaders());
In both cases, after call method restTemplate.exchange(...) it corrupts.
Any ideas?
Thanks a lot
After a lot of research, the problem is related to charset of RestTemplate.
If I add this line on #Bean configuration, the problem is solved:
#Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
/*** THE SAVERY LINE ***/
restTemplate.getMessageConverters().add(0, new StringHttpMessageConverter(Charset.forName("UTF-8")));
/*** ***/
List<ClientHttpRequestInterceptor> interceptors
= restTemplate.getInterceptors();
if (CollectionUtils.isEmpty(interceptors)) {
interceptors = new ArrayList<>();
}
interceptors.add(new HeaderLoginInterceptor());
restTemplate.setInterceptors(interceptors);
return restTemplate;
}
After that, works normally.

Spring RestTemplate and HttpClient connection pooling limit

I'm using RestTemplate on my jsf web server, and with these configuration I often reach limit of connection to routes so soon (exp:/ap-domain/get-all).
How can I release connection to avoid limit?
This is my config class:
#Configuration
public class RestTemplateConfig {
#Bean
public PoolingHttpClientConnectionManager poolingHttpClientConnectionManager() {
PoolingHttpClientConnectionManager result = new PoolingHttpClientConnectionManager();
result.setMaxTotal(100);
result.setDefaultMaxPerRoute(20);
return result;
}
#Bean
public CloseableHttpClient httpClient() throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException {
CloseableHttpClient result = HttpClientBuilder.create()
.setConnectionManager(poolingHttpClientConnectionManager())
.setDefaultRequestConfig(requestConfig())
.setSSLSocketFactory(csf)
.build();
return result;
}
#Bean
public RestTemplate restTemplate() throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException {
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient());
RestTemplate restTemplate = new RestTemplate(requestFactory);
restTemplate.setErrorHandler(new RestResponseErrorHandler());
List<ClientHttpRequestInterceptor> interceptors = restTemplate.getInterceptors();
if (interceptors.isEmpty()) {
interceptors = new ArrayList<>();
}
interceptors.add(new RestTemplateHeaderInterceptor());
restTemplate.setInterceptors(interceptors);
return restTemplate;
}
}
My RestUtil to call api:
#Component
public class RestUtil {
private static RestTemplate restTemplate;
#Autowired
public RestUtil(RestTemplate restTemplate) {
RestUtil.restTemplate = restTemplate;
}
public static <T> MessagesResponse<T> exchange(String url, HttpMethod method, Object requestObject, String token, ParameterizedTypeReference<MessagesResponse<T>> type) {
...
}
}

Spring Boot OAuth2RestTemplate and HTTP Client Metrics

Actuator manages the instrumentation of RestTemplate, we only have to inject RestTemplateBuilder to create RestTemplate bean:
#Configuration
public class HttpClientConfiguration {
private final RestTemplateBuilder restTemplateBuilder;
// let Actuator manages the instrumentation
public HttpClientConfiguration(RestTemplateBuilder restTemplateBuilder) {
this.restTemplateBuilder = restTemplateBuilder;
}
#Bean // let Sleuth intercept requests
public RestTemplate createRestTemplate() {
return restTemplateBuilder.build();
}
}
How to create an OAuth2RestTemplate bean (built-in OAuth2) with the HTTP Client Mertics support?
I just found a solution:
#Configuration
#EnableOAuth2Client
public class OAuthClientConfig {
private final MetricsRestTemplateCustomizer metricsRestTemplateCustomizer;
public OAuthConfig(MetricsRestTemplateCustomizer metricsRestTemplateCustomizer) {
this.metricsRestTemplateCustomizer=metricsRestTemplateCustomizer;
}
#Bean
public OAuth2RestTemplate createOAuth2RestTemplate() {
OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(
getClientCredentialsResourceDetails(),
new DefaultOAuth2ClientContext());
metricsRestTemplateCustomizer.customize(restTemplate);
return restTemplate;
}
private ClientCredentialsResourceDetails getClientCredentialsResourceDetails() {
ClientCredentialsResourceDetails rd = new ClientCredentialsResourceDetails();
rd.setClientId("my-client-id");
rd.setClientSecret("my-client-secret");
rd.setAccessTokenUri("https://my-idp.server/oauth2/token");
return rd;
}
}
This is a late answer, but I was looking for something similar and found an alertnaive way.
The Spring Boot configured RestTemplateBuilder adds more than just the HTTP Client Mertics (see RestTemplateAutoConfiguration). If you don't want to lose those, you can still use the auto-configured RestTemplateBuilder to configure your OAuth2RestTemplate as follows:
#Configuration
public class OAuthClientConfig {
#Bean
public OAuth2RestTemplate createOAuth2RestTemplate(RestTemplateBuilder builder) {
// Create and setup your OAuth2RestTemplate:
OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(....);
// let the auto-configure builder configure your template:
return builder.configure(restTemplate);
}
}

Spring boot Oauth2 : Token relay from a client using Feign, Ribbon, Zull and Eureka to a ressource

I have an oauth2 client that get a token from an authorization server successfully. (not always has been the case but now it is... :))
The client, the zuul gateway and the resource server are all registered in Eureka.
My client use a Proxy to access to a remote ressource service named microservice-files.
#RestController
#FeignClient(name = "zuul-server")
#RibbonClient(name = "microservice-files")
public interface ProxyMicroserviceFiles {
#GetMapping(value = "microservice-files/root")
FileBean getUserRoot();
}
So I'd like to relay the token to Zull and then to the resource server.
I can relay the token this way to contact Zuul and apparently the load balancing is managed too (I've just test I didn't know and it's great) also zuul can relay the token, but it's not very convenient I'd prefer the previous approach.
#EnableConfigurationProperties
#SpringBootApplication
#EnableFeignClients("com.clientui")
public class ClientUiApplication {
#Bean
public OAuth2RestOperations restOperations(
OAuth2ProtectedResourceDetails resource,
OAuth2ClientContext context) {
return new OAuth2RestTemplate(resource, context);
}
public static void main(String[] args) {
SpringApplication.run(ClientUiApplication.class, args);
}
}
here is the test controler
#Controller
public class ClientController {
#Autowired
private RestOperations restOperations;
#RequestMapping("/root")
public ResponseEntity userRootTest() {
String rootUrl = "http://localhost:9004/microservice-files/root";
return restOperations.getForEntity(rootUrl,FileBean.class);
}
}
If I correctly understand your problem then you can use a RequestInterceptor to add a token in each request by the feign. In order to do it you can use the next configuration:
#Bean
public RequestInterceptor oauth2FeignRequestInterceptor(OAuth2ClientContext oauth2ClientContext,
OAuth2ProtectedResourceDetails resource) {
return new OAuth2FeignRequestInterceptor(oauth2ClientContext, resource);
}
#Bean
protected OAuth2ProtectedResourceDetails resource() {
AuthorizationCodeResourceDetails resource = new AuthorizationCodeResourceDetails();
resource.setAccessTokenUri("http://127.0.0.1:9000/auth/login");
resource.setUserAuthorizationUri("http://127.0.0.1:9000/auth/authorize");
resource.setClientId("my-client");
resource.setClientSecret("my-secret");
return resource;
}
This is what I did to make it work.
#Bean(name = "oauth2RestTemplate")
#LoadBalanced
public OAuth2RestTemplate restTemplate(SpringClientFactory clientFactory) {
OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resourceDetails());
RibbonLoadBalancerClient ribbonLoadBalancerClient = new RibbonLoadBalancerClient(clientFactory);
LoadBalancerInterceptor loadBalancerInterceptor = new LoadBalancerInterceptor(ribbonLoadBalancerClient);
ClientCredentialsAccessTokenProvider accessTokenProvider = new ClientCredentialsAccessTokenProvider();
accessTokenProvider.setInterceptors(Arrays.asList(loadBalancerInterceptor));
restTemplate.setAccessTokenProvider(accessTokenProvider);
return restTemplate;
}
public ClientCredentialsResourceDetails resourceDetails() {
ClientCredentialsResourceDetails clientCredentialsResourceDetails = new ClientCredentialsResourceDetails();
clientCredentialsResourceDetails.setId("1");
clientCredentialsResourceDetails.setClientId("my-ms");
clientCredentialsResourceDetails.setClientSecret("123");
clientCredentialsResourceDetails.setAccessTokenUri("http://oauth-server/oauth/token");
clientCredentialsResourceDetails.setScope(Arrays.asList("read"));
clientCredentialsResourceDetails.setGrantType("client_credentials");
return clientCredentialsResourceDetails;
}

Resttemplate - how to post object with HAL representation?

When attempting to post to a Spring-Data-Rest web service via RestTemplate, the JSON representation of my domain object is being converted to a full blown JSON object that isn't in HAL representation. My assumption here is that I need to register the Jackson2HalModule as a deserializer though am not sure how to do that considering I register it to the objectMapper. The serialization works correctly when calling GET on the webservice, just not for POST/PUT:
Request outputBuffer field:
{
"id" : 1,
"name" : "Name",
"description" : "",
"childObject" : {
"id" : 1,
"name" : "test"
}
}
Rest Template configuration:
#Bean
public ObjectMapper objectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JodaModule());
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
objectMapper.setDateFormat(new ISO8601DateFormat());
objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
objectMapper.registerModule(new Jackson2HalModule());
return objectMapper;
}
public void configureMessageConverters(
List<HttpMessageConverter<?>> messageConverters) {
MappingJackson2HttpMessageConverter jsonMessageConverter = new MappingJackson2HttpMessageConverter();
jsonMessageConverter.setObjectMapper(objectMapper());
jsonMessageConverter.setSupportedMediaTypes(MediaType
.parseMediaTypes("application/hal+json,application/json"));
messageConverters.add(jsonMessageConverter);
}
#Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
configureMessageConverters(messageConverters);
restTemplate.setMessageConverters(messageConverters);
return restTemplate;
}
Request Headers:
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
Calling method:
ResponseEntity<DomainObject> responseEntity =
restTemplate.exchange(this.getBaseUri() + resolveResource(), HttpMethod.POST, new HttpEntity(domainObject,createHttpHeaders(tenantId)), DomainObject.class);
I think you should not register your own ObjectMapper. Spring creates it for you and also registers all the modules needed. So I would just try to remove your ObjectMapper bean.
If you need to customize the ObjectMapper you could use a Jackson2ObjectMapperBuilder. See the documentation for more details - http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#howto-customize-the-jackson-objectmapper
#Bean
public Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder() {
return Jackson2ObjectMapperBuilder //
.json() //
.locale(ENGLISH) //
.timeZone("UTC") //
.indentOutput(true) //
.serializationInclusion(NON_NULL) //
.featuresToDisable(WRITE_DATES_AS_TIMESTAMPS, FAIL_ON_UNKNOWN_PROPERTIES) //
;
}
I would also let spring take care of the message converters:
So let spring inject them when you create the RestTemplate - so something like this:
#Bean
public RestTemplate restTemplate(List<HttpMessageConverter<?>> messageConverters) {
RestTemplate restTemplate = new RestTemplate();
restTemplate.setMessageConverters(messageConverters);
return restTemplate;
}

Resources