Setting Security cookie using RestTemplate - java

I am trying to call a Restful JSON service using RestTemplate and Jackson json convertor. Now in order to call the service I need to pass in a Security cookie. I can achieve this by using URLConnection (See the code below)
URL url= new URL("https://XXXXXXXX");
URLConnection yc = url.openConnection();
yc.setRequestProperty("SecurityCookie", ssocookie.getValue());</code>
Whats the parallel for this in RestTemplate? Here is a code snippet which I have been using to call a Restful Service using RestTemplate:
RestTemplate rest = new RestTemplate();
InputBean input = new InputBean();
input.setResource("SampleResource");
HttpEntity<InputBean > entity = new HttpEntity<InputBean>(input);
ResponseEntity<OutputBean> response1 = rest.postForEntity(
"https://XXXXXXXXX",
entity, OutputBean.class);</code>
I can not figure out how to pass the security cookie while using RestTemplate to call the service. Any help on this would be great.

I wrote a blog post that explains how to do this using request headers:
http://springinpractice.com/2012/04/08/sending-cookies-with-resttemplate/
Here's the code:
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.add("Cookie", "JSESSIONID=" + session.getValue());
HttpEntity requestEntity = new HttpEntity(null, requestHeaders);
ResponseEntity rssResponse = restTemplate.exchange(
"https://jira.example.com/sr/jira.issueviews:searchrequest-xml/18107/SearchRequest-18107.xml?tempMax=1000",
HttpMethod.GET,
requestEntity,
Rss.class);
Rss rss = rssResponse.getBody();

You can access the underlying HttpURLConnection used by RestTemplate by wiring your RestTemplate up with a custom ClientHttpRequestFactory, which lets you access the underlying connection to set headers, properties, etc. The ClientHttpRequestFactory is used by RestTemplate when creating new connections.
In particular, you can extend the SimpleClientHttpRequestFactory implementation and override the prepareConnection() method:
public class YourClientHttpRequestFactory extends SimpleClientHttpRequestFactory {
#Override
protected void prepareConnection(HttpURLConnection connection, String httpMethod) {
connection.setRequestProperty("SecurityCookie", ssocookie.getValue());
}
}

This is how it has worked for us
requestHeaders.add("Cookie", "JSESSIONID=" + session.getValue());

Related

Consuming a StreamingResponseBody with Spring

I've got a simple web-service that stream a file using a StreamingResponseBody.
The definition looks like this:
#GetMapping("/files/{filename}")
public ResponseEntity<StreamingResponseBody> download(#PathVariable String filename) {
...
StreamingResponseBody responseBody = out -> {
...
}
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentLength(byteArray.length);
return new ResponseEntity(responseBody, httpHeaders, HttpStatus.OK);
}
It works well, but now, I need to consume it in a client application.
I'm using spring to consume it, but I can't find a way to read the stream and write it to a file as it flows...
I tryied using feign but it seems it doesn't support it.
I tryied using restTemplate but I can't make it work...
Does spring support streaming client side ?
Does anybody know how to do this ?
Perhaps using pure java API ?
Thanks a lot for your help !
You can use Apache Http Client (org.apache.httpcomponents:httpclient:4.5.12):
URI uri = new URIBuilder()
.setScheme(scheme)
.setHost(host)
.setPort(port)
.setPath(url)
.build();
HttpUriRequest request = RequestBuilder.get(uri).build();
try (CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse httpResponse = httpClient.execute(request);
InputStream inputStream = httpResponse.getEntity().getContent()) {
// Do with stream whatever you want, for example put it to File using FileOutputStream and 'inputStream' above.
}

How to set HTTP Protocol version when using Spring restTemplate.exchange?

I'm using Spring Framework restTemplate.exchange method to perform HTTPS GET call and map the response object to the entity class like below.
The target server gives success response only when I send the request with HTTP protocol version 1.1 (HTTP1.1). I verified it using the curl command. RestTemplate uses HTTP1.2 by default. I'm using Apache HTTP Client v4.3 here in which httpClient.getParams is deprecated. So I'm not able to set the HTTP protocol version using getParams method. Is there any other way to set the http protocol version as HTTP1.1 when using restTemplate.exchange method?
#Autowired
CloseableHttpClient httpClient;
#Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory());
return restTemplate;
}
#Bean
public HttpComponentsClientHttpRequestFactory clientHttpRequestFactory() {
HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new
HttpComponentsClientHttpRequestFactory();
clientHttpRequestFactory.setHttpClient(httpClient);
return clientHttpRequestFactory;
}
public ResponseEntity<Foo> getResponse(){
RestTemplate restTemplate = restTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity entity = new HttpEntity(headers);
ResponseEntity<Foo> response = restTemplate
.exchange(fooResourceUrl, HttpMethod.GET, entity, Foo.class);
return response;
}

Why is my Session gone when using RestTemplate to access Spring MVC?

I am trying to access a Spring MVC app. That uses a CSRF Token. I do an initial GET to receive the Token. Then add it to my POST with my JSESSIONID. However, during debug the Server app doesn't find my JSESSIONID. And therefore, doesn't authenticate my token, and gives me 403.
I can't tell but it looks like my GET JSESSIONID doesn't get saved in the server HTTP Session repository.
Is there a way, to validate:
The session is in the server context?
Am I sending the correct header data?
Here's my code:
public String testLogin() {
ResponseEntity<String> response =
restTemplate.getForEntity(LOGIN_RESOURCE_URL, String.class);
List<String> cookies = new ArrayList<String>();
cookies = response.getHeaders().get("Set-Cookie");
String[] firstString = cookies.get(0).split("=|;");
String jsessionPart = firstString[1];
String[] secondString = cookies.get(1).split("=|;");
String tokenPart = secondString[1];
BasicCookieStore cookieStore = new BasicCookieStore();
BasicClientCookie cookie = new BasicClientCookie("JSESSIONID",
jsessionPart);
cookie.setDomain(".mydomain.com");
cookie.setPath("/");
cookieStore.addCookie(cookie);
BasicClientCookie cookie2 = new BasicClientCookie("X-XSRF-TOKEN",
tokenPart);
cookie2.setDomain(".mydomain.com");
cookie2.setPath("/");
cookieStore.addCookie(cookie2);
HttpClient client = HttpClientBuilder
.create()
.setDefaultCookieStore(cookieStore)
.disableRedirectHandling()
.build();
HttpComponentsClientHttpRequestFactory factory = new
HttpComponentsClientHttpRequestFactory();
factory.setHttpClient(client);
RestTemplate postTemplate = new RestTemplate(factory);
HttpEntity<?> requestEntity = new HttpEntity<Object>(body, headers);
ResponseEntity<String> response = postTemplate.exchange(loginUserUrl,
HttpMethod.POST, requestEntity,String.class);
To your code sample I added user name and password plus changed the content type. The 403 still happens whether i sent content type or not:
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
// if you need to pass form parameters in request with headers.
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
try {
map.add( URLEncoder.encode("username", "UTF-8"),
URLEncoder.encode("userdev", "UTF-8") );
map.add(URLEncoder.encode("password", "UTF-8"),
URLEncoder.encode("devpwd","UTF-8") );
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>
(map, headers);
ResponseEntity<String> response =
this.restTemplate(builder).exchange(RESOURCE_URL, HttpMethod.POST,
requestEntity, String.class);
Instead of messing around with cookies yourself let the framework, Apache HttpClient, handle this for you. Configure the RestTemplate to work with a properly configured HttpClient.
Something like this should do the trick
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.requestFactory(this::requestFactory)
.build();
}
#Bean
public HttpComponentsClientHttpRequestFactory requestFactory() {
RequestConfig defaultRequestConfig = RequestConfig.custom()
.setCookieSpec(CookieSpecs.DEFAULT)
.setExpectContinueEnabled(true)
.build();
CloseableHttpClient httpClient = HttpClientBuilder.create()
.setDefaultCookieStore(new BasicCookieStore())
.setDefaultRequestConfig(defaultRequestConfig).build();
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(httpClient);
return requestFactory;
}
This will configure the RestTemplate to use a HttpClient that stores cookies in a CookieStore in between requests. Reuse the configured RestTemplate and don't create a new one because you might need it.

Spring RestTemplate UnsupportedOperationException with Collections$UnmodifiableCollection.add(Unknown Source)

This is our rest template config
#Bean
public RestTemplate infoBloxRestTemplate() {
RestTemplate restTemplate=new RestTemplate();
ArrayList<ClientHttpRequestInterceptor> interceptors = new ArrayList<>();
interceptors.add(httpBasicAuthenticationInterceptor());
restTemplate.setInterceptors(interceptors);
restTemplate.getMessageConverters().add(jacksonConverter());
restTemplate.setRequestFactory(genericHttpRequestFactory());
return restTemplate;
}
We are trying to make a POST call which works successfully with Postman and returns proper response.
final HttpHeaders headers = new HttpHeaders();
headers.add("Accept", "application/json");
headers.add("Content-Type", "application/json");
HttpEntity<Object> httpEntity = new HttpEntity<Object>(record, headers);
StringBuilder uri = new StringBuilder(infobloxRestClient.createUrl("/record:host"));
infobloxRestClient.getRestTemplate().exchange(uri.toString(), HttpMethod.POST, httpEntity, String.class);
But this POST invocation fails with below error. Here is my stack trace:
com.sun.xml.ws.server.sei.TieHandler createResponse
SEVERE: null
java.lang.UnsupportedOperationException
at java.util.Collections$UnmodifiableCollection.add(Unknown Source)
at org.springframework.http.HttpHeaders.add(HttpHeaders.java:558)
at com.test.externalinterfaces.HTTPBasicAuthenticationInterceptor.intercept(HTTPBasicAuthenticationInterceptor.java:30)
at org.springframework.http.client.InterceptingClientHttpRequest$RequestExecution.execute(InterceptingClientHttpRequest.java:81)
at org.springframework.http.client.InterceptingClientHttpRequest.executeInternal(InterceptingClientHttpRequest.java:67)
at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:46)
at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:49)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:488)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:452)
Any help on this regard will be very helpful.
To get more people know this issue:
#Sameer, I found the problem is you're using HttpHeaders ,you can try to build your header with this code
MultiValueMap<String, String> headers =new LinkedMultiValueMap<String, String>();
And not use the HttpHeaders and then in your HttpEntity to build the object entity as
new HttpEntity<Object>(record, headers);
Then it should solve problem .
Try creating the RestTemplate object in the following way,
ResTemplate restTemplate = new RestTemplate(new HttpComponentsClientHttpRequestFactory());
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("Authorization", authorizationProperty); //Can also use add(String headerName, String headerValue)
This way it should works.

Making authenticated POST requests with Spring RestTemplate for Android

I have a RESTful API I'm trying to connect with via Android and RestTemplate. All requests to the API are authenticated with HTTP Authentication, through setting the headers of the HttpEntity and then using RestTemplate's exchange() method.
All GET requests work great this way, but I cannot figure out how to accomplish authenticated POST requests. postForObject and postForEntity handle POSTs, but have no easy way to set the Authentication headers.
So for GETs, this works great:
HttpAuthentication httpAuthentication = new HttpBasicAuthentication("username", "password");
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setAuthorization(httpAuthentication);
HttpEntity<?> httpEntity = new HttpEntity<Object>(requestHeaders);
MyModel[] models = restTemplate.exchange("/api/url", HttpMethod.GET, httpEntity, MyModel[].class);
But POSTs apparently don't work with exchange() as it never sends the customized headers and I don't see how to set the request body using exchange().
What is the easiest way to make authenticated POST requests from RestTemplate?
Ok found the answer. exchange() is the best way. Oddly the HttpEntity class doesn't have a setBody() method (it has getBody()), but it is still possible to set the request body, via the constructor.
// Create the request body as a MultiValueMap
MultiValueMap<String, String> body = new LinkedMultiValueMap<String, String>();
body.add("field", "value");
// Note the body object as first parameter!
HttpEntity<?> httpEntity = new HttpEntity<Object>(body, requestHeaders);
ResponseEntity<MyModel> response = restTemplate.exchange("/api/url", HttpMethod.POST, httpEntity, MyModel.class);
Slightly different approach:
MultiValueMap<String, String> headers = new LinkedMultiValueMap<String, String>();
headers.add("HeaderName", "value");
headers.add("Content-Type", "application/json");
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
HttpEntity<ObjectToPass> request = new HttpEntity<ObjectToPass>(objectToPass, headers);
restTemplate.postForObject(url, request, ClassWhateverYourControllerReturns.class);
I was recently dealing with an issue when I was trying to get past authentication while making a REST call from Java, and while the answers in this thread (and other threads) helped, there was still a bit of trial and error involved in getting it working.
What worked for me was encoding credentials in Base64 and adding them as Basic Authorization headers. I then added them as an HttpEntity to restTemplate.postForEntity, which gave me the response I needed.
Here's the class I wrote for this in full (extending RestTemplate):
public class AuthorizedRestTemplate extends RestTemplate{
private String username;
private String password;
public AuthorizedRestTemplate(String username, String password){
this.username = username;
this.password = password;
}
public String getForObject(String url, Object... urlVariables){
return authorizedRestCall(this, url, urlVariables);
}
private String authorizedRestCall(RestTemplate restTemplate,
String url, Object... urlVariables){
HttpEntity<String> request = getRequest();
ResponseEntity<String> entity = restTemplate.postForEntity(url,
request, String.class, urlVariables);
return entity.getBody();
}
private HttpEntity<String> getRequest(){
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Basic " + getBase64Credentials());
return new HttpEntity<String>(headers);
}
private String getBase64Credentials(){
String plainCreds = username + ":" + password;
byte[] plainCredsBytes = plainCreds.getBytes();
byte[] base64CredsBytes = Base64.encodeBase64(plainCredsBytes);
return new String(base64CredsBytes);
}
}
Very useful
I had a slightly different scenario where I the request xml was itself the body of the POST and not a param. For that the following code can be used - Posting as an answer just in case anyone else having similar issue will benefit.
final HttpHeaders headers = new HttpHeaders();
headers.add("header1", "9998");
headers.add("username", "xxxxx");
headers.add("password", "xxxxx");
headers.add("header2", "yyyyyy");
headers.add("header3", "zzzzz");
headers.setContentType(MediaType.APPLICATION_XML);
headers.setAccept(Arrays.asList(MediaType.APPLICATION_XML));
final HttpEntity<MyXmlbeansRequestDocument> httpEntity = new HttpEntity<MyXmlbeansRequestDocument>(
MyXmlbeansRequestDocument.Factory.parse(request), headers);
final ResponseEntity<MyXmlbeansResponseDocument> responseEntity = restTemplate.exchange(url, HttpMethod.POST, httpEntity,MyXmlbeansResponseDocument.class);
log.info(responseEntity.getBody());

Categories