festner
festner

Reputation: 31

clear Cookies with RestTemplate before make HTTP-call

I use Spring-Boot 2.0.2 RestTemplate to make following scenario:

I'm a Rest-Consumer (client), which :

  1. first need to log in on a Spring-Security-Check
  2. 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));
    }
}

Upvotes: 3

Views: 9320

Answers (1)

i v s narayana
i v s narayana

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

Related Questions