I use Spring-Boot 2.0.2 RestTemplate
to make following scenario:
I'm a Rest-Consumer (client), which :
- first need to log in on a Spring-Security-Check
- then make a second call to get the data.
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.).
Now my problem/question:
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));
}
}