Kerberos client - kerberoRestTemplate not working - spring-boot

Im trying to consume an api which is authenticating with Kerberos. I have referred the below spring documentation related to KerberosRestTemplate.reference link, im passing the correct keytab file and the userPrincipal values as mentioned in the reference doc. But still im receiving 401 from the server.
But when I execute the kinit command in the terminal it receives a ticket from KDC and with that, im able to execute the curl command and get a working response.
KerberosRestTemplate kerberosRestTemplate = new KerberosRestTemplate("svc_dfsd.keytab", "svc_dfsd#sswe.AD");
String url="https://wexample.com:20550/aggr_subscriber_summary_hbase/03434809824";
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.TEXT_XML));
HttpEntity<String> entity = new HttpEntity<String>("parameters", headers);
ResponseEntity<String> response = kerberosRestTemplate.exchange(url, HttpMethod.GET, entity, String.class);
Can you suggest any other better approach to do this or fix this. All your comments are highly appreciated!!!

Troubleshooting Kerberos might be tricky since the errors are often misleading and Java implementation does a lot if implicit actions (canonicalization of URLs, etc.).
I suggest trying Kerb4J library which allows you to generate the kerberos token explicitly:
SpnegoClient spnegoClient = SpnegoClient.loginWithKeyTab("svc_dfsd#sswe.AD", "svc_dfsd.keytab");
SpnegoContext context = spnegoClient.createContext("https://wexample.com"); // Will result in HTTP/wexample.com SPN
RestTemplate restTemplate = new RestTemplate();
String url="https://wexample.com:20550/aggr_subscriber_summary_hbase/03434809824";
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.TEXT_XML));
headers.add("Authorization", context.createTokenAsAuthroizationHeader());
HttpEntity<String> entity = new HttpEntity<String>("parameters", headers);
ResponseEntity<String> response = restTemplate .exchange(url, HttpMethod.GET, entity, String.class);
If default SPN resolution works for you, you can also use SpnegoRestTemplate from this Kerb4J:
SpnegoClient spnegoClient = SpnegoClient.loginWithKeyTab("svc_dfsd#sswe.AD", "svc_dfsd.keytab");
SpnegoRestTemplate spnegoRestTemplate = new SpnegoRestTemplate(spnegoClient);
String url="https://wexample.com:20550/aggr_subscriber_summary_hbase/03434809824";
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.TEXT_XML));
HttpEntity<String> entity = new HttpEntity<String>("parameters", headers);
ResponseEntity<String> response = spnegoRestTemplate.exchange(url, HttpMethod.GET, entity, String.class);
Disclaimer: I'm the author of Kerb4J

Related

How to retrieve all Cookies from REST Template?

I am working on a project where I want to send a POST REST call and get the response and in the response, there are few cookies. among those, I want to get X-Bonita-API-Token and pass it to the frontend.
I used String set_cookie = httpHeaders.getFirst(HttpHeaders.SET_COOKIE); this return only the first cookie but i need the second.
my code
final String uri = "http://localhost:8080/bonita/loginservice";
RestTemplate template = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity<ApiRequest> entity = new HttpEntity<>(apiRequest, headers);
template.getMessageConverters().add(getMappingJackson2HttpMessageConverter());
HttpEntity<String> response = template.exchange(uri, HttpMethod.POST, entity,String.class);
HttpHeaders httpHeaders = response.getHeaders();
String set_cookie = httpHeaders.getFirst(HttpHeaders.SET_COOKIE);
Here are the returned cookies.
can anyone help me to retrieve X-Bonita-API-Token?
Thanks.

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.

How to not escape form body characters in Spring RestTemplate when making a POST request?

I have following code where I am making a POST form request. The request body contains username and password. Password contains # characters, which is replaced by RestTemplate with %40 and I am getting "unauthorized" error as the password is wrong now.
Following is the debug info from bufferOutput(request body)
merchant_id=firstname+de-lastname%40gmail.com&password=%40Password
Here is the code snippet that is making the call.
MultiValueMap<String, String> formData = new LinkedMultiValueMap<String, String>();
String url = "SOME_URL";
formData.add("username", "xay#gmail.com");
formData.add("password", "#name321");
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity<MultiValueMap<String,String>> requestEntity = new HttpEntity<MultiValueMap<String,String>>(formData,headers);
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters()
.add(0, new StringHttpMessageConverter(Charset.forName("UTF-8")));
restTemplate.exchange(url, HttpMethod.POST, requestEntity, Authentication.class,"321");
The question is how to tell restTemplate not to escape body data ?
Note that the request has to be a post form request and I can not use UriComponentsBuilder to fix the problem.
I think you need to add a FormHttpMessageConverter to your RestTemplate rather than a StringHttpMessageConverter.

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

HTTP get with headers using RestTemplate

How can I send a GET request using the Spring RestTemplate?
Other questions have used POST, but I need to use GET.
When I run this, the program continues to work, but it seems that the network is clogged because this is in an AsyncTask, and when I try to run another asynctask after I click on the button for this one, they won't work.
I tried doing
String url = "https://api.blah.com/2.0/search/cubes?w=jdfkl&whitespace=1";
MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>();
map.add("Bearer", accessToken);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); //copied this from somewhere else, not sure what its for
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<MultiValueMap<String, String>>(map, headers);
HttpMessageConverter<String> stringConverter = new StringHttpMessageConverter();
FormHttpMessageConverter formConverter = new FormHttpMessageConverter();
List<HttpMessageConverter<?>> msgConverters = new ArrayList<HttpMessageConverter<?>>();
msgConverters.add(formConverter);
msgConverters.add(new MappingJacksonHttpMessageConverter());
msgConverters.add(stringConverter);
template.setMessageConverters(msgConverters);
//SetSearchResponseData is my custom class to store the incoming JSON
ResponseEntity<SetSearchResponseData> result = template.exchange(url, HttpMethod.GET, request, SetSearchResponseData.class);
//If I was using post, i could have done SetSearchResponseDataresponse = restTemplate.postForObject(url, request, SetSearchResponseData.class);
The RestTemplate getForObject() method does not support setting headers. The solution is to use the exchange() method.
So instead of restTemplate.getForObject(url, String.class, param) (which has no headers), use
HttpHeaders headers = new HttpHeaders();
headers.set("Header", "value");
headers.set("Other-Header", "othervalue");
...
HttpEntity<Void> requestEntity = new HttpEntity<>(headers);
ResponseEntity<String> response = restTemplate.exchange(
url, HttpMethod.GET, requestEntity, String.class, param);
Finally, use response.getBody() to get your result.
This question is similar to this question.
Take a look at the JavaDoc for RestTemplate.
There is the corresponding getForObject methods that are the HTTP GET equivalents of postForObject, but they doesn't appear to fulfil your requirements of "GET with headers", as there is no way to specify headers on any of the calls.
Looking at the JavaDoc, no method that is HTTP GET specific allows you to also provide header information. There are alternatives though, one of which you have found and are using. The exchange methods allow you to provide an HttpEntity object representing the details of the request (including headers). The execute methods allow you to specify a RequestCallback from which you can add the headers upon its invocation.
The getForObject() method of RestTemplate does not support setting headers. you can use this
syntax:
restTemplate.exchange(url endpoint,
HttpMethod.GET,entity, params)
public List<Employee> getListofEmployee()
{
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
HttpEntity<String> entity = new HttpEntity<String>(headers);
ResponseEntity<List<Employee>> response = restTemplate.exchange("http://hello-server/rest/employees",
HttpMethod.GET,entity, new ParameterizedTypeReference<List<Employee>>() {});
return response.getBody(); //this returns List of Employee
}

Resources