37

I'm writing a simple client in Java to allow reusable use of proprietary virus scanning software accessible through a RESTful API. To upload a file for scanning the API requires a POST for Connect, followed by a POST for Publishing the file to the server. In the response to the Connect POST there are cookies set by the server which need to be present in the subsequent POST for publishing the file. I'm currently using Spring RestTemplate in my client.

My question is how do I access the cookies in the response to forward back to the server with the subsequent POST? I can see that they are present in the header that is returned but there are no methods on the ResponseEntity to access them.

Michael Lihs
  • 7,460
  • 17
  • 52
  • 85
Tom
  • 3,006
  • 6
  • 33
  • 54

9 Answers9

30

RestTemplate has a method in which you can define Interface ResponseExtractor<T>, this interface is used to obtain the headers of the response, once you have them you could send it back using HttpEntity and added again.

 .add("Cookie", "SERVERID=c52");

Try something like this.

String cookieHeader = null;

new ResponseExtractor<T>(){
      T extractData(ClientHttpResponse response) {
        response.getHeaders();
      }
}

Then

  HttpHeaders headers = new HttpHeaders();
  headers.add("Cookie", cookieHeader );

  ResponseEntity<byte[]> response = restTemplate.exchange("http://example.com/file/123",
      GET,
      new HttpEntity<String>(headers),
      byte[].class);

Also read this post

Michael Lihs
  • 7,460
  • 17
  • 52
  • 85
Koitoer
  • 18,778
  • 7
  • 63
  • 86
  • So in other words I just treat them like any other header? I wasn't sure if there was a special way to handle them with the RestTemplate where they get automatically added to the subsequent responses or something. – Tom Apr 04 '14 at 13:36
  • Yes you are right and I don't think make this automatically can be achieve with the resttemplate as it is. You should take the headers and re send them, if you can achieve automatically don't forget to tell me your trick =) – Koitoer Apr 04 '14 at 16:09
  • @Koitoer I could make it work automatically by adding a single line :) RequestConfig requestConfig = RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD_STRICT).build(); Look at my answer below for entire code fragment. – vivek4348 Jan 15 '21 at 03:28
  • please add this post to the answer, https://stackoverflow.com/questions/5796078/setting-security-cookie-using-resttemplate – mattsmith5 Jul 26 '23 at 23:27
  • posted question here, https://stackoverflow.com/questions/76775831/setting-security-cookie-using-resttemplate-with-domain-and-as-secure – mattsmith5 Jul 27 '23 at 00:31
21

I've solved the problem by creating an interceptor which stores a cookie and puts it in next requests.

public class StatefulRestTemplateInterceptor implements ClientHttpRequestInterceptor {
    private String cookie;

    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
        if (cookie != null) {
            request.getHeaders().add(HttpHeaders.COOKIE, cookie);
        }
        ClientHttpResponse response = execution.execute(request, body);

        if (cookie == null) {
            cookie = response.getHeaders().getFirst(HttpHeaders.SET_COOKIE);
        }
        return response;
    }
}

Set the interceptor for your RestTemplate:

@Bean
public RestTemplate restTemplate(RestTemplateBuilder templateBuilder) {
    return templateBuilder
            .requestFactory(new BufferingClientHttpRequestFactory(new HttpComponentsClientHttpRequestFactory()))
            .interceptors(new StatefulRestTemplateInterceptor())
            .build();
}
Ilya Lysenko
  • 1,772
  • 15
  • 24
  • 2
    N.B. `RestTemplateBuilder` is only present in `spring-boot`. Can also use `setInterceptors(new StatefulRestTemplateInterceptor)` on the `RestTemplate` object directly, just a heads up for anyone reading this. Solution works like a charm though. – Hasan Aslam May 28 '20 at 15:59
10

Small update to handle sessions in a complete test with 'java.net.HttpCookie' Object.

@Thanks Shedon

import java.io.IOException;
import java.net.HttpCookie;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RequestCallback;
import org.springframework.web.client.ResponseExtractor;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;

/**
 * @link https://stackoverflow.com/questions/22853321/resttemplate-client-with-cookies
 */
@Component
public class RestTemplateWithCookies extends RestTemplate {

    private final List<HttpCookie> cookies = new ArrayList<>();

    public RestTemplateWithCookies() {
    }

    public RestTemplateWithCookies(ClientHttpRequestFactory requestFactory) {
        super(requestFactory);
    }

    public synchronized List<HttpCookie> getCoookies() {
        return cookies;
    }

    public synchronized void resetCoookies() {
        cookies.clear();
    }

    private void processHeaders(HttpHeaders headers) {
        final List<String> cooks = headers.get("Set-Cookie");
        if (cooks != null && !cooks.isEmpty()) {
            cooks.stream().map((c) -> HttpCookie.parse(c)).forEachOrdered((cook) -> {
                cook.forEach((a) -> {
                    HttpCookie cookieExists = cookies.stream().filter(x -> a.getName().equals(x.getName())).findAny().orElse(null);
                    if (cookieExists != null) {
                        cookies.remove(cookieExists);
                    }
                    cookies.add(a);
                });
            });
        }
    }

    @Override
    protected <T extends Object> T doExecute(URI url, HttpMethod method, final RequestCallback requestCallback, final ResponseExtractor<T> responseExtractor) throws RestClientException {
        final List<HttpCookie> cookies = getCoookies();

        return super.doExecute(url, method, new RequestCallback() {
            @Override
            public void doWithRequest(ClientHttpRequest chr) throws IOException {
                if (cookies != null) {
                    StringBuilder sb = new StringBuilder();
                    for (HttpCookie cookie : cookies) {
                        sb.append(cookie.getName()).append(cookie.getValue()).append(";");
                    }
                    chr.getHeaders().add("Cookie", sb.toString());
                }
                requestCallback.doWithRequest(chr);
            }

        }, new ResponseExtractor<T>() {
            @Override
            public T extractData(ClientHttpResponse chr) throws IOException {
                processHeaders(chr.getHeaders());
                return responseExtractor.extractData(chr);
            }
        });
    }

}
GerardNorton
  • 157
  • 1
  • 3
5

You need to use exchange method of RestTemplate of Java Spring framework.

Read this tutorial: http://codeflex.co/java-rest-client-get-cookie/

ybonda
  • 1,546
  • 25
  • 38
JavaGoPro
  • 232
  • 3
  • 7
4

It can be achieved automatically like what browsers do with the below code.

Reference: 1. https://hc.apache.org/httpclient-3.x/cookies.html

  1. https://hc.apache.org/httpcomponents-client-ga/tutorial/html/statemgmt.html
@Bean
public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
    RequestConfig requestConfig = RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD_STRICT).build();        
    HttpClient  httpClient = HttpClientBuilder.create()
            .setDefaultRequestConfig(requestConfig)         
            .build();   
    
    RestTemplate restTemplate = restTemplateBuilder
            .requestFactory(
                    () -> {
                        HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
                        requestFactory.setHttpClient(httpClient);
                        return new BufferingClientHttpRequestFactory(requestFactory);
                    })
            .basicAuthentication("username", "password")
            .build();
    
    return restTemplate;
}
vivek4348
  • 435
  • 1
  • 5
  • 15
3

i've wrote a simple class that extends RestTemplate and handles cookies.

import java.io.IOException;
import java.net.URI;
import java.util.List;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.web.client.RequestCallback;
import org.springframework.web.client.ResponseExtractor;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;

public class RestTemplateWithCookies extends RestTemplate {

    private List<String> cookies = null;

    public RestTemplateWithCookies() {
    }

    public RestTemplateWithCookies(ClientHttpRequestFactory requestFactory) {
        super(requestFactory);
    }

    private synchronized List<String> getCoookies() {
        return cookies;
    }

    private synchronized void setCoookies(List<String> cookies) {
        this.cookies = cookies;
    }

    public synchronized void resetCoookies() {
        this.cookies = null;
    }

    private void processHeaders(HttpHeaders headers) {
        final List<String> cookies = headers.get("Set-Cookie");
        if (cookies != null && !cookies.isEmpty()) {
            setCoookies(cookies);
        }
    }

    @Override
    protected <T extends Object> T doExecute(URI url, HttpMethod method, final RequestCallback requestCallback, final ResponseExtractor<T> responseExtractor) throws RestClientException {
        final List<String> cookies = getCoookies();

        return super.doExecute(url, method, new RequestCallback() {
            @Override
            public void doWithRequest(ClientHttpRequest chr) throws IOException {
                if(cookies != null) {
                    for(String cookie : cookies) {
                        chr.getHeaders().add("Cookie", cookie);
                    }
                }
                requestCallback.doWithRequest(chr);
            }

        }, new ResponseExtractor<T>() {
            @Override
            public T extractData(ClientHttpResponse chr) throws IOException {
                processHeaders(chr.getHeaders());
                return responseExtractor.extractData(chr);
            }
        });
    }

}
Shedon
  • 162
  • 1
  • 9
0

To get more browser like behavior you can use this interceptor:

import java.io.IOException;
import java.net.HttpCookie;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;

public class CookieHandlingClientHttpRequestInterceptor implements ClientHttpRequestInterceptor {

  private static final Logger LOG = LoggerFactory.getLogger(CookieHandlingClientHttpRequestInterceptor.class);

  private final Map<String, HttpCookie> cookies = new HashMap<>();

  @Override
  public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
    List<String> cookiesForRequest = cookies.values().stream()
        .filter(cookie -> cookie.getPath() != null && request.getURI().getPath().startsWith(cookie.getPath()))
        .map(HttpCookie::toString)
        .collect(Collectors.toList());
    LOG.info("Using cookies: {}", cookiesForRequest);
    request.getHeaders().addAll(HttpHeaders.COOKIE, cookiesForRequest);

    ClientHttpResponse response = execution.execute(request, body);

    List<String> newCookies = response.getHeaders().get(HttpHeaders.SET_COOKIE);
    if (newCookies != null) {
      List<HttpCookie> parsedCookies = newCookies.stream().flatMap(rawCookie -> HttpCookie.parse(HttpHeaders.SET_COOKIE + ": " + rawCookie).stream()).collect(Collectors.toList());
      LOG.info("Extracted cookies from response: {}", parsedCookies);
      parsedCookies.forEach(newCookie -> cookies.put(newCookie.getName(), newCookie));
    }

    return response;
  }
}

And keep in mind that by default RestTemplate follows redirects for GET requests. In this case the above interceptor is bypassed.

DAN
  • 507
  • 1
  • 7
  • 16
0

Code snippet of RestTemplate GET Call with Cookie

        final String url = "url";
        String set_cookie = "cookie value";

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.add("Cookie", set_cookie);
        HttpEntity request = new HttpEntity(headers);
        
        ResponseEntity<Profile> response = restTemplate.exchange(url, HttpMethod.GET, request, Profile.class);
abhinav kumar
  • 1,487
  • 1
  • 12
  • 20
-1
                    package zuulx;

                import java.net.URI;
                import java.net.URISyntaxException;

                import org.springframework.http.HttpEntity;
                import org.springframework.http.HttpHeaders;
                import org.springframework.http.HttpMethod;
                import org.springframework.http.ResponseEntity;
                import org.springframework.web.client.RestTemplate;

                public class CookieTest {
                /**
                 * 
                 * array(1) {
                  ["aaa"]=>
                  string(2) "11"
                }


                 * @param args
                 * @throws URISyntaxException
                 */
                    public static void main(String[] args) throws URISyntaxException {
                        
                        HttpHeaders headers = new HttpHeaders();
                          headers.add("Cookie", "aaa=11" );
                          
                        
                           RestTemplate restTemplate = new RestTemplate();
                           
                    //  URI url= new URI("http://localhost:9088/cktest");
                    //  System.out.println( restTemplate.getForObject(url, String.class));  
                        
                          String url = "http://localhost/showck.php";
                          url="http://localhost:9088/cktest";
                        ResponseEntity response = restTemplate.exchange(url,
                                  HttpMethod.GET,
                                  new HttpEntity (headers) ,String.class);
                          
                          System.out.println(response.getBody());
                        
                        
                        //http://localhost/showck.php

                    }

                }