Send the HttpSession through restTemplate - spring-boot

How to send the HttpSession through the restTemplate to next microservice consider below exmple.
We have two miceoservices call A and B. We are calling some restTemplate call to service B.
But in the Service B we are getting attributes through httpServletRequest.getSession().getAttribute("name"); this is the limitations in service B.
So how to pass the HttpSession to service B from A?

You can pass the session through headers like this:
HttpHeaders headers = new HttpHeaders();
headers.add("Your-Session-Header-Name", serialize(session));
ResponseEntity<UUID> exchange = restTemplate.exchange(url, HttpMethod.GET,new HttpEntity<Void>(headers), UUID.class);
And read session from headers at the server side

Related

TraceID for a request is not travelling between services

I am calling a microservice from a microservice and expecting the traceID (given by sleuth) initiated from base service should travel to the called services as implanted sleuth with zipkin.
Here is the call to the service
RestTemplate restTemplate = new RestTemplate();
logger.info("ApplicationController: controllerMessage() called: " + properties.getType() +
" " + properties2.getMode());
String uri = "http://localhost:8089/poc1/message";
//String uri = "http://google.com";
HttpHeaders headers = new HttpHeaders();
//headers.add("Authorization", authToken);
HttpEntity request = new HttpEntity(headers);
ResponseEntity<String> response = restTemplate.exchange(
uri,
HttpMethod.GET,
request,
String.class
);
Expecting that the same traceID printed in the logs of above service will be printed in the logs of called service. In this case, same trace id should print in the logs of http://localhost:8089/poc1/message. However this is not happening.
Using 2.7.5 version of spring boot and 2021.0.4 of spring cloud.
Any clue what is wrong here?
Expecting the same traceID generated by the initial request should print in the logs of called service.
You're creating RestTemplate via new. Please create it as a bean and inject it to your code.
#Bean
RestTemplate restTemplate() {
return new RestTemplate();
}
We're writing it in the docs with a big exclamation mark here https://docs.spring.io/spring-cloud-sleuth/docs/current/reference/html/integrations.html#sleuth-http-client-rest-template-integration
Let me paste that for your convenience
You have to register RestTemplate as a bean so that the interceptors get injected. If you create a RestTemplate instance with a new keyword, the instrumentation does NOT work.

How to use OAuth2RestTemplate having only tokenValue?

The application starts when other application calls the starting endpoint with the access token as a paremeter. The access token is a type of string.
Then I have to call a few other endpoints where the authentication is based on that token.
Is it possible to create OAuth2RestTemplate to make requests having only tokenValue withouth access token uri?
As far as I know, you can achieve this by implementing the AccessTokenProvider interface and setting it:
https://docs.spring.io/spring-security/oauth/apidocs/org/springframework/security/oauth2/client/token/AccessTokenProvider.html
Although, the OAuth2RestTemplate is exactly to simplify the use of this kind of flow for you. If your use will be straight forward maybe it's better for you to use the default RestTemplate and use the HTTP specification. So, for this, you should use an Authorization header of type Bearer. Like this:
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Bearer " + tokenValue);
And use this header in your requests, like this:
HttpEntity<String> request = new HttpEntity<String>(headers);
ResponseEntity<BodyClass> response = restTemplate.exchange(url, HttpMethod.GET, request, BodyClass.class);
BodyClass body = response.getBody();
Hope it helps.

OAuth2RestTemplate TCP connections

I'm using a Spring OAuth2RestTemplate with ClientCredentialsResourceDetails to acquire an API authorization token. The authorization server and the API endpoints are hidden behind the same load balancers (LB). We have an issues where the first connection to the API endpoint, after acquiring the token, fails with a 404 error message but subsequent calls to the same API endpoint with the same token are successful. I believe the LB is miss-configured in some way but we've been asked if we could try using separate TCP sessions for the acquisition of the token and then the REST call. Is there a way to get the Spring RestTemplate to do this?
UPDATE
Here's how I create and configure the template:
#Bean
public OAuth2RestTemplate oauth2RestTemplate(
#Value("${token.uri}") final String tokenUri,
#Value("${token.clientId:client}") final String clientId,
#Value("${token.secret:secret}") final String clientSecret,
#Value("${token.scope:platform}") final String scope,
final MappingJackson2HttpMessageConverter customJackson2HttpMessageConverter)
{
ClientCredentialsResourceDetails rd = new
ClientCredentialsResourceDetails();
rd.setAuthenticationScheme(AuthenticationScheme.header);
rd.setAccessTokenUri(tokenUri);
rd.setClientId(clientId);
rd.setClientSecret(clientSecret);
rd.setScope(Arrays.asList(scope));
OAuth2RestTemplate rt = new OAuth2RestTemplate(rd);
List<HttpMessageConverter<?>> converters = rt.getMessageConverters();
converters.add(customJackson2HttpMessageConverter);
rt.setMessageConverters(converters);
return rt;
}
and here's the call to the api:
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
headers.set("Connection", "close"); // hmm, gets replace by keep-alive on the token api request!
HttpEntity<String> entity = new HttpEntity<String>(headers);
ResponseEntity<MyObject[]> response = restTemplate.exchange(
"http://example.com/api/v1/rest/method",
HttpMethod.GET, entity, MyObject[].class);
Thanks.
Try adding the Connection request header with value as close while sending your request using resttemplate. This should force the TCP connection to be closed after each request. Not very performant though.
HttpHeaders headers = new HttpHeaders();
headers.set("Connection", "close");
This is only for the "but we've been asked if we could try using separate TCP sessions for the acquisition of the token and then the REST call." part of your question. It will not help resolve your 404 (that does seem to be an LB issue).
UPDATE: Since you're using OAuth2RestTemplate, create a ClientHttpRequestInterceptor which injects the header.
public class ConnectionCloseInterceptor implements ClientHttpRequestInterceptor {
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
HttpHeaders headers = request.getHeaders();
headers.add("Connection", "close");
return execution.execute(request, body);
}
}
Use it in your rest template (OAuth2RestTemplate extends RestTemplate so below applies to both) like so (when you create the rest template bean):
List<ClientHttpRequestInterceptor> currentInterceptors = new ArrayList<>(restTemplate.getInterceptors()); //Don't want to lose the other interceptors!
currentInterceptors.add(new ConnectionCloseInterceptor()); //Add ours
restTemplate.setInterceptors(currentInterceptors);

Unable to Retrieve cookies when request is called using Spring RestTemplate

I have two web applications. There is a HttpSession at second application and first application knows the sessionid of it.
I am making a request from First application to second application using RestTemplate by adding its session id to headers.
When the request is received at the second application, I am trying to read all its cookies from request. But, I only see the JSESSIONID cookie and don't get all the cookies that I set previously.
Suppose, I access the same url on the browser manually, I see all those cookies retrieved.
Request made in First application:
String sessionId = "6iuvgwy5ceqzwlxh646qo0ms";//SessionId of second application
String url = "http://example.com/data/retrieve";
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.add("Cookie", "JSESSIONID=" + sessionId);
HttpEntity<String> requestEntity = new HttpEntity<String>(null, requestHeaders);
ResponseEntity<String> responseEntity = restTemplate.exchange(url,
HttpMethod.GET, requestEntity, String.class);
If this can't be done using Spring RestTemplate, please suggest me if there is any other way of doing it.
There is no session cookie. Cookie is just a header.
You lind of reset the header to have only JSESSIONID.
To fix it change the logic a bit
You somehow get the session id (I guess from some request). Get not only the session but all the rest cookie as well and add them to the requestHeaders when you call the restTemplate.

Spring RestTemplate session

I'm trying to use spring rest template to do a post request to login in.
When I receive the response in my first request i store my session id which is received via cookie. I retrieve it in a set-cookie response header which i get via:
//first request
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
LinkedMultiValueMap<String, Object> mvm = new LinkedMultiValueMap<String, Object>();
mvm.add("LoginForm_Login", "login");
mvm.add("LoginForm_Password", "password");
ResponseEntity<String> result = restTemplate.exchange(uriDWLogin, HttpMethod.POST, requestEntity, String.class);
result.getHeaders().get("Set-Cookie").stream().forEach(System.out::println);
then in every subsequent request i set the Cookie request header with the values received in the first request:
//subsequent request
RestTemplate restTemplate = new RestTemplate(); HttpHeaders headers = new HttpHeaders();
headers.set("Cookie",cookies.stream().collect(Collectors.joining(";")));
HttpEntity<String> entity = new HttpEntity<String>(headers);
RestTemplate.exchange("http://url", HttpMethod.POST, entity, String.class);
all goes well for the second request, but I can not keep the session for the others requests
You will need to use some kind of cache to store your access token.
When you'll be accessing downstream service, you take the token from cache. If cache doesn't contain token, you will authenticate and retrieve it and store to cache first.
Caching is always tricky topic, because it has to be thread-safe. I would try to avoid servlet sessions. You are consuming service, not being consumed.
There are various caching options, but as you are already using Spring, spring caching may be good fit. Take a look at this Spring Cache guide to start.

Resources