Unauthorized 401 error for GET call on stockitem - spring

I am accessing Acumatica API using Java Spring Resttemplate. The POST call to the Login endpoint works fine. But the next call to get StockItems gets a 401 Unauthorized error.
org.springframework.web.client.HttpClientErrorException: 401 Unauthorized at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:91) at org.springframework.web.client.RestTemplate.handleResponseError(RestTemplate.java:615) at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:573) at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:544) at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:465) at
Now this works fine when I use Postman and the Chrome Restlet client. I noticed that in Restlet client the GET call to the API passes a session cookie that was set by the API in the login call. I tried passing the response headers from the login call in the GET request. But I still get a 401. I am using the standard resttemplate configuration.
HttpEntity<Credentials> entity = new HttpEntity<Credentials>(credentials, headers);
ResponseEntity<String> response = restTemplate.exchange("https://xxxx.acumatica.com/entity/auth/login",
HttpMethod.POST, entity, String.class);
HttpHeaders rHeaders = response.getHeaders();
String set_cookie = rHeaders.getFirst(rHeaders.SET_COOKIE);
if (LOG.isInfoEnabled()) { LOG.info("Response: " + response.toString()); }
if (LOG.isInfoEnabled()) { LOG.info("Set-Cookie: " + set_cookie); }
HttpEntity<String> entity2 = new HttpEntity<String>(response.getHeaders());
ResponseEntity<String> response2 = restTemplate.exchange("https://usell.acumatica.com/entity/Default/6.00.001/StockItem?$expand=Attributes,WarehouseDetails", HttpMethod.GET, entity2, String.class);
How did Acumatica API client using Java get around this problem?

I was not setting all the cookies.. this is all I had to do
List<String> cookies = response.getHeaders().get(HttpHeaders.SET_COOKIE);
HttpHeaders requestHeaders = new HttpHeaders();
for (String cookie : cookies) {
requestHeaders.add("Cookie", cookie);
}

Related

How to read response with Content-Type text/plain;charset=UTF-8 using RestTemplate

I have an API (/get-sas-token) that is returning a response with Content-Type=text/plain;charset=UTF-8.
In Postman when I hit this API it returns as sig=PAd%2By7yzue0G%2FVeMKbwvR%2F%2B5a3X8CUTablCIhS3uCuk%3D&s
The code for this API is
#GetMapping("/get-sas-token/{containerName}")
public String getSASToken(#PathVariable("containerName") String containerName)
throws InvalidKeyException, URISyntaxException, StorageException {
CloudBlobContainer appcontainer = config.blobClient().getContainerReference(containerName);
return appConfiguration.generateSASToken(appcontainer);
}
My other microservice is trying to call this RestAPI using RestTemplate via a get request. The response I'm getting in Postman has instead of special characters like �������{J�J��t�\b�`$ؐ#������iG# some weird stuff. So I guess somethings wrong with the character encoding.
P.S : If I remove the Accept-Encoding(gzip, deflate, br) header from Postman it works.How can i get it working in my code
ResponseEntity<String> imageUrl = restTemplate.exchange(
fileServiceUrl + "/get-sas-token/" + containerReference, HttpMethod.GET, request, String.class);
logger.info("imageUrl body-------->>" + imageUrl.getBody());
It prints
imageUrl body-------->>?`I?%&/m?{J..................
I have tried all the possible soultions but nothing works for me
tried this::
template.getMessageConverters()
.add(0, new StringHttpMessageConverter(Charset.forName("UTF-8")));
ResponseEntity<Object> response = template.exchange(endpoint, method, entity,
Object.class);
Also this::
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.set("Accept", "application/json");

RestTemplate call returns 401 Unauthorized

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?

400 Bad Request for Spring RestTemplate and GET on Salesforce.com Connected Apps

Having an issue to create a basic API to the Salesforce.com object queries using Connected Apps and a Spring RestTemplate client. The inital authentication step using a POST operation is working fine and returns the Salesforce instance URL as well as the required access-token. The following GET operation fails with an Error 400. The URL and access-token were both validated by a Chrome POSTMAN plugin, that in combination returns a valid JSON response.
It is also worth to mention that the traditional implementation has not worked well and that "setAccept" was a later attempt to try to solve the parsing of the mediatype.
Below the code in error:
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Collections.singletonList(MediaType.parseMediaType("application/json")));
headers.add("User-Agent", USER_AGENT);
headers.add("Authorization", "Bearer " + accessToken);
HttpEntity<String> request = new HttpEntity<String>(headers);
ResponseEntity<String> responseBodyForGet = restRetrieveSalesforceData
.exchange(url, HttpMethod.GET, request, String.class);
The solution found to fix the 400 bug at the Spring RestTemplate GET operation is the following:
URI targetUrl = UriComponentsBuilder.fromUriString(url).build().toUri();
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Bearer " + accessToken);
HttpEntity<String> request = new HttpEntity<String>(headers);
logger.info("Requesting GET from: " + url);
ResponseEntity<String> responseBodyForGet = restRetrieveSalesforceData.exchange(targetUrl,
HttpMethod.GET, request, String.class);
I've added the solution to the report posted above. Observe the use of the first statement UriComponentsBuilder, which solved the 400 issue at the GET.

Spring RestTemplate receives "401 Unauthorized"

I am using the following to retrieve JSON via RestTemplate in Spring 4:
protected DocInfoResponse retrieveData(String urlWithAuth) {
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Basic " + auth.getSig());
HttpEntity<String> request = new HttpEntity<String>(headers);
ResponseEntity<DocInfoResponse> response = restTemplate.exchange(urlWithAuth, HttpMethod.GET, request, DocInfoResponse.class);
return response.getBody();
}
I used the same code (with different response class) to successfully get a JSON doc from the same site (with different parameters to get a different doc).
When I execute the above code I receive the following stack trace (in part):
Caused by: org.springframework.web.client.HttpClientErrorException: 401 Unauthorized
at
org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:91) ~[spring-web-4.3.7.RELEASE.jar:4.3.7.RELEASE]
Can anyone point me to why this might be receiving the exception?
I found that my issue originally posted above was due to double encryption happening on the auth params. I resolved it by using UriComponentsBuilder and explicitly calling encode() on the the exchange().
SyncResponse retrieveData(UriComponentsBuilder builder) {
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.set("Accept", MediaType.APPLICATION_JSON_VALUE);
HttpEntity<String> request = new HttpEntity<String>(headers);
ResponseEntity<SyncResponse> response = restTemplate.exchange(builder.build().encode().toUri(), HttpMethod.GET, request, SyncResponse.class);
return response.getBody();
}
My UriComponentsBuilder was built using:
UriComponentsBuilder buildUrl(String urlString) {
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(urlString);
return auth.appendAuth(builder);
}
(The auth.appendAuth() adds additional .queryParams() needed by the target service in urlString.)
The call to execute this was retrieveData(buildUrl(urlString));.
After investigating on my own problem, I realized that FireFox RESTClient was successful because I was connected to the target URL. The Basic Auth I thought I was using, was not so basic after all.
Eventually, I read the doc of the app i was trying to connect to and realized they propose a connection token mechanism. Now it works.
After reading your code, I say it looks quite OK, although I'm not sure what is your object auth on which you call getSig.
First things first: try to access your service from any client, like a web browser, a PostMan or RESTClient. Make sure you successfully retrieve your infos WITHOUT being connected to your app!!!
Depending on the result, I say you should, either try to encrypt manually your Authorization token (you'll easilly find posts on this site to show you how to) or try another connection mechanism.
The process of creating the Authorization header is relatively straightforward for Basic Authentication, so it can pretty much be done manually with a few lines of code:
HttpHeaders createHeaders(String username, String password){
return new HttpHeaders() {{
String auth = username + ":" + password;
byte[] encodedAuth = Base64.encodeBase64(
auth.getBytes(Charset.forName("US-ASCII")) );
String authHeader = "Basic " + new String( encodedAuth );
set( "Authorization", authHeader );
}};
}
Then, sending a request becomes just as simple:
RestTemplate restTemplate = new RestTemplate();
restTemplate.exchange
(uri, HttpMethod.POST, new HttpEntity<T>(createHeaders(username, password)), clazz);
https://www.baeldung.com/how-to-use-resttemplate-with-basic-authentication-in-spring#manual_auth

How to access Spring REST API in JHipster with Spring RestTemplate

I have set up JHipster like described on its homepage with some entities. Frontend with AngularJS works great and also the API page, lets me test my services as expected.
Now I am trying to write a REST-Client using Spring's RestTemplate like this:
public List<SomeEntity> getAllEntities(){
URI uri = URI.create("http://localhost:8080/api/entities");
HttpHeaders httpHeaders = this.createHeaders("admin", "admin")
ResponseEntity<SomeEntity[]> responseEntity = restTemplate.exchange(uri, HttpMethod.GET, new HttpEntity<SomeEntity>(httpHeaders), SomeEntity[].class);
return Arrays.asList(responseEntity.getBody());
}
private HttpHeaders createHeaders(final String username, final String password ){
HttpHeaders headers = new HttpHeaders(){
{
String auth = username + ":" + password;
byte[] encodedAuth = Base64.encode(
auth.getBytes(Charset.forName("US-ASCII")) );
String authHeader = "Basic " + new String( encodedAuth );
set( "Authorization", authHeader );
}
};
headers.add("Content-Type", "application/json");
headers.add("Accept", "application/json");
return headers;
}
But this results in the following error:
[WARN] org.springframework.web.client.RestTemplate - GET request for "http://localhost:8080/api/entities" resulted in 401 (Unauthorized); invoking error handler
Now I am not sure, if and how I need to adapt my HttpHeaders or if my simple basic-auth handling approach at all is wrong.
The way you authenticate is wrong, it seems you chose session authentication when generating your app, so this requires form-based auth not http basic auth and it requires being able to store session cookie and CSRF cookie so most likely using commons http client.
Maybe choosing xauth token authentication when generating your app would be simpler.
Once you get this working you will have CORS issues as soon as your client won't run on same host as your JHipster app.

Resources