I have a RestTemplate call to an API (get). This call, is the only we have of GET type, and go through a proxy. It seems that sometimes during a week, the call returns a 403 Forbidden with this exception: "sun.security.validator.ValidatorException"
We have a certificate between Spring and the API, but the certificate works fine (the application returns thousands of "200 ok" during a day).
But sometimes, only this call (not others that are POST) returns a "403 Forbidden".
We have done:
Launch Jmeter with curl through the proxy (everything seems ok)
Disable the TrustStore only to test (the result is ko)
This is the RestTemplate code:
SSLConnectionSocketFactory socketFactory;
socketFactory = new SSLConnectionSocketFactory(new SSLContextBuilder()
.loadTrustMaterial(ResourceUtils.getFile(this.trustStorePath), this.trustStorePassword.toCharArray())
.loadKeyMaterial(ResourceUtils.getFile(this.keyStorePath), this.keystorePassword.toCharArray(),
this.keystorePassword.toCharArray())
.build(), NoopHostnameVerifier.INSTANCE);
CloseableHttpClient client = HttpClients.custom().setSSLSocketFactory(socketFactory).setProxy(host)
.disableCookieManagement().disableRedirectHandling().build();
ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(client);
RestTemplate restTemplateVar = new RestTemplate(requestFactory);
And this is the call:
response = this.restTemplate.getForEntity(this.host, String.class);
Could the number of concurrent connections be the cause?
Why only with GET and sometimes?
And the last one: If we change RestTemplate by Httpconnection, the result could be different?
Thank in advance
Setting this properties works fine (it depends on your metrics)
.setMaxConnTotal(1000)
.setMaxConnPerRoute(40)
CloseableHttpClient client = HttpClients.custom()
.setSSLSocketFactory(socketFactory)
.setProxy(host)
.disableCookieManagement()
.disableRedirectHandling()
.setMaxConnTotal(1000)
.setMaxConnPerRoute(40)
.build();
Related
I'm building an application that need to call an endpoint using NTLM authentication. My approach is that I try to use the Apache HttpComponents for the NTLM authentication and integrate the Spring WebClient with it. However, the WebClient doesn't seem to send any request at all. There's no errors but the response won't be returned.
Below is my code:
BasicCredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(new AuthScope(null, -1), new NTCredentials(username, password, computername, domain));
HttpAsyncClientBuilder clientBuilder = HttpAsyncClients.custom();
clientBuilder.setDefaultRequestConfig(RequestConfig.DEFAULT);
ClientHttpConnector connector = new HttpComponentsClientHttpConnector(client);
WebClient.builder().clientConnector(connector).build();
ResponseDto response = webClient.post()
.uri("http://myhost:8080/api/notification/add")
.body(Mono.just(request), RequestDto.class)
.retrieve()
.bodyToMono(ResponseDto.class).block();
i have problem with api from dhl, i create GET api from dhl, when print in console, result will print, but when using browser i got response like this :
com.squareup.okhttp.internal.http.RealResponseBody#68bd3d26
this my code :
#RequestMapping("/getData")
public String getAcc() throws IOException
{
OkHttpClient client = new OkHttpClient();
MediaType mediaType = MediaType.parse("application/json");
HttpUrl httpUrl = new HttpUrl.Builder()
.scheme("https")
.host("api-eu.dhl.com")
.addPathSegment("track")
.addPathSegment("shipments")
.addQueryParameter("trackingNumber", "cencored")
.addQueryParameter("service", "express")
.build();
Request request = new Request.Builder()
.addHeader("content-type", "application/json")
.addHeader("Connection", "close")
.addHeader("DHL-API-Key", "cencored")
.addHeader("ConsumerKey", "cencored")
.addHeader("ConsumerSecret", "cencored")
.removeHeader("Content-Encoding")
.removeHeader("Content-Length")
.url(httpUrl) // <- Finally put httpUrl in here
.build();
response = client.newCall(request).execute();
System.out.println(response.body().string());
return this.response.body().toString();
}
solved...
this is weird, but work for me.
so we can't call "response.body().string();" twice.
This is the correctly way to consume a soap webservice with spring boot: https://spring.io/guides/gs/consuming-web-service/
Follow this tutorial and it works fine.
Background
I am trying to consume a REST endpoint hosted on IBM Cloud API from my SpringBoot application using RestTemplate. I am using the following snippet to make the call:
RestTemplate send = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setCacheControl(CacheControl.noCache());
headers.set("x-ibm-client-id", clientId);
headers.set("x-ibm-client-secret", clientSecret);
HttpEntity<BodyEntity> httpEntity = new HttpEntity<>(bodyEntity, headers);
send.exchange(ENDPOINT_URL, HttpMethod.POST, httpEntity, Object.class);
I used the following snippet to configure RestTemplate
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build();
}
Problem
Using this snippet, when the call is made I receive 401 Unauthorized. When I made the same call using Postman, I received correct response from server without any problem.
Since I received 401 response code I set to further investigate the request by logging headers and body and other parts of request.
I implemented ClientHttpRequestInterceptor to log outgoing requests to further debug the issue and added this interceptor to my RestTemplate config as follows:
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
// new code
builder.interceptors(new LoggingClientHttpRequestInterceptor());
return builder.build();
}
After making the request again, I could see in the log that the outgoing call contained all details as it should e.g. Headers and Body were correct.
After this, I changed the whole thing to use Apache HTTP Client as follows:
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpPost httpPost = new HttpPost(URL);
String reqString = "BODY";
httpPost.setEntity(new StringEntity(reqString, ContentType.APPLICATION_JSON));
httpPost.setHeader("accept", "application/json");
httpPost.setHeader("content-type", "application/json");
httpPost.setHeader("cache-control", "no-cache");
httpPost.setHeader("x-ibm-client-id", clientId);
httpPost.setHeader("x-ibm-client-secret", clientSecret);
CloseableHttpResponse response = httpClient.execute(httpPost);
try {
System.out.println("Response status: " + response.getStatusLine());
HttpEntity entity1 = response.getEntity();
System.out.println("Response :" + entity1.toString());
} finally {
response.close();
}
Using the snippet above, I executed the request and received correct response.
Question
Why RestTemplate call returns and error whereas HttpClient returns correct response?
Do I need to further configure RestTemplate?
What have I missed?
Has anyone performed a sucessfull Shopify authentication (and used their APIs) via Spring ?
I have been trying by using the RestTemplate , but can't login :
RestTemplate restTemplate = new RestTemplate();
String result = restTemplate.getForObject("https://apikey:password#shopname.myshopify.com/admin/shop.json",String.class);
logger.info(result);
Unfortunately, I keep getting this :
org.springframework.web.client.HttpClientErrorException: 401 Unauthorized
Whereas it works fine with the browser!
Do i need to locally import the shopify certificate ? if yes, it has been already done via the keytool.
Is it possible to authenticate via RestTemplate as I am doing, or should I need to go with Auth0 ?
Feel free to post a working snippet if any of you suceeded :)
Thanks a lot!
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.set("X-Shopify-Access-Token", "xxxxx");
//headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
HttpEntity<String> entity = new HttpEntity<String>(headers);
String result = restTemplate.exchange("https://apikey:password#shopname.myshopify.com/admin/shop.json", HttpMethod.GET, entity, String.class).getBody();
I am building out a web service which proxies and does some slight manipulation of HTTP requests. I'm handling requests going to multiple hosts of the same type but of which I don't know of until run time (I consume a web service that gives the host IPs). Each host that I interact with has different credentials (Basic-Auth, fetched from a non-local database, credentials change periodically). The way I handle things today is pretty naive. For every request, I am constructing a new RestTemplate like so:
public static RestOperations getRestOperations(int timeout, String username, String password)
{
RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(timeout).setConnectTimeout(timeout).setSocketTimeout(timeout).build();
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password));
CloseableHttpClient httpclient = HttpClients.custom()
.setDefaultCredentialsProvider(credsProvider)
.setDefaultRequestConfig(requestConfig)
.build();
ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(
httpclient);
return new RestTemplate(requestFactory);
}
So each Controller method always starts out with:
UsernamePassword userPass = credentialService.getCredentials(request.getRemoteHost())
RestOperations restOps = getRestOperations(userPass.getUser(), userPass.getPass(), TIMEOUT_IN_MILLIS);
It seems to me that since I'm constructing a new RestTemplate with each request that any previous connections that have been made between my server and the host are not being reused.
Is this the case? If so, then it seems I will need some sort of RestTemplateFactory which can cache RestTemplate instances based on the host IP address so that connections can be reused. However if I do that, then I need some mechanism that makes sure that the credentials haven't changed and to check and update credentials if they do change. Is there a better solution?