Reputation: 31
I use Spring-Boot 2.0.2 RestTemplate
to make following scenario:
I'm a Rest-Consumer (client), which :
First, I was thinking to make the auth call and manually read the JSESSIONID Cookie from SET-COOKIE and set it on the second call in the header. Here my first try:
The RestClient :
@Service
public class RestClient {
private static final String ENDPOINT_DATA = "/data";
private static final String ENDPOINT_SECURITY_CHECK = "/j_spring_security_check";
private static final String HTTP_HEADER_KEY_SET_COOKIE = "Set-Cookie";
private static final String HTTP_HEADER_KEY_COOKIE = "Cookie";
private static final String PROPERTY_SPRING_SECURITY_USER = "j_username";
private static final String PROPERTY_SPRING_SECURITY_PASS = "j_password";
private final RestTemplate restTemplate;
private final RestConfig restConfig;
@Autowired
public RestClient(RestTemplateBuilder restTemplateBuilder, final RestConfig restConfig) {
notNull(restTemplateBuilder, "restTemplateBuilder must not be null!");
this.restTemplate = restTemplateBuilder
.additionalCustomizers(new NoRedirectionHandlingRestTemplateCostumizer())
.build();
notNull(restConfig, "openIdConfig must not be null!");
this.restConfig = restConfig;
}
public String getData() {
final String jSessionCockie = jSpringSecurityLogin();
return getAuthorizeCode(jSessionCockie);
}
private String jSpringSecurityLogin() {
// read config
final String fullLoginUri = restConfig.getUrl() + ENDPOINT_SECURITY_CHECK;
final String user = restConfig.getUser();
final String password = restConfig.getPassword();
// Build entity that is send
final HttpHeaders headers = new HttpHeaders();
final String body = PROPERTY_SPRING_SECURITY_USER + "=" + user + "&" + PROPERTY_SPRING_SECURITY_PASS + "=" + password;
final HttpEntity<String> toSend = new HttpEntity<>(body, headers);
final String jSessionIdCockie;
final ResponseEntity<String> response = restTemplate.postForEntity(fullLoginUri, toSend, String.class);
// Get String "JSESSIONID=XXXX". If there are other Cookies, propably will fail.
if (HttpStatus.FOUND.equals(response.getStatusCode()) && response.getHeaders().containsKey(HTTP_HEADER_KEY_SET_COOKIE)) {
jSessionIdCockie = response.getHeaders().get(HTTP_HEADER_KEY_SET_COOKIE).get(0);
} else {
throw new Error();
}
return jSessionIdCockie;
}
private String getAuthorizeCode(final String jSessionCockie) {
// read config
final String fullDataUri = restConfig.getUrl() + ENDPOINT_DATA;
// Build entity that is send
final HttpHeaders headers = new HttpHeaders();
headers.add(HTTP_HEADER_KEY_COOKIE, jSessionCockie);
final HttpEntity<Void> toSend = new HttpEntity<>(headers);
final ResponseEntity<String> response = restTemplate.exchange(fullDataUri, HttpMethod.GET, toSend, String.class);
if (HttpStatus.OK.equals(response.getStatusCode()) && response.hasBody()) {
return response.getBody();
} else {
return "";
}
}
}
To be complete, here the Costumizer used in the Constructor:
class NoRedirectionHandlingRestTemplateCostumizer implements RestTemplateCustomizer {
@Override
public void customize(RestTemplate restTemplate) {
final HttpClient httpClient = HttpClientBuilder.create()
.disableRedirectHandling()
.build();
restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory(httpClient));
}
}
Now, when I make some Function-Tests with Wiremock I see folowing in the wiremock-reqeust History for the Data-call:
{
...
"headers":
{
"Cookie": [
"JSESSIONID=axcvueiornxniuwherwuieoiun,asdpfoiu",
"JSESSIONID=axcvueiornxniuwherwuieoiun,asdpfoiu"
],
...
},
"cookies":
{
"JSESSIONID": [
"axcvueiornxniuwherwuieoiun,asdpfoiu",
"axcvueiornxniuwherwuieoiun,asdpfoiu"
]
}
...
}
Wait - JSESSIONID is set 2 times. Cool REST handles it for me !
Second try: I can remove the Cookie handing. And it works.
But first and second try have on problem: After the first call of RestClient.getData()
all following calls to the login-Endpoint also have the JSESSIONID set. I don't know how the Rest-Producer can handle this (Session-timeout e.c.t.).
I would like to force RestTemplate
to forget/clear all Cookies before making the login-call (reason description before). Is there an possibility ? I did not found anything that worked.
My Workaround right now is to disable the Cookie management of RestTemplate
and do all manually like in first try. The disabling of the CookieManagment can be done over the RestTemplateCustomizer
:
class NoRedirectionHandlingRestTemplateCostumizer implements RestTemplateCustomizer {
@Override
public void customize(RestTemplate restTemplate) {
final HttpClient httpClient = HttpClientBuilder.create()
.disableRedirectHandling()
.disableCookieManagement()
.build();
restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory(httpClient));
}
}
Upvotes: 3
Views: 9320
Reputation: 120
As it is mentioned in the question, first we need to disable cookie management in the following way
CloseableHttpClient httpClient = HttpClients.custom() .setConnectionManager(cm) .setRetryHandler(retryHandler) .setKeepAliveStrategy(connectionKeepAliveStrategy) .disableCookieManagement() .evictExpiredConnections().evictIdleConnections(10, TimeUnit.MINUTES) .build();
And set "Cookie" header like any other header.
public ResponseEntity getContent(String url, Map<String, String> headers, Map<String, String> cookies){ HttpHeaders requestHeaders = new HttpHeaders();
// add headers
for(String key : headers.keySet()){
requestHeaders.set(key, headers.get(key));
}
// add cookies
List<String> allCookieValues = new ArrayList<>();
for(String key : cookies.keySet()){
String value = cookies.get(key);
String cookieString = MessageFormat.format("{0}={1}", key,value);
allCookieValues.add(cookieString);
}
requestHeaders.addAll(HttpHeaders.COOKIE, allCookieValues);
HttpEntity<String> httpEntity = new HttpEntity<String>(requestHeaders);
UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(url);
URI uri = builder.build(true).toUri();
ResponseEntity responseEntity = null;
try {
responseEntity = restTemplate.exchange(uri, HttpMethod.GET, httpEntity, byte[].class);
...... ......
From the response, if you want to read we can read the cookies in the following way
List responseCookies = responseEntity.getHeaders().get(HttpHeaders.SET_COOKIE); Map<String, String> cookieNameValue = new HashMap<>();
if(requestCookies != null){
cookieNameValue.putAll(requestCookies);
}
for(String cookie : responseCookies){
String[] cookieParts = cookie.split(";")[0].split("=");
cookieNameValue.put(cookieParts[0], cookieParts[1]);
}
return cookieNameValue;
Upvotes: 2