So I'm currently developing a Spring boot MS that needs to connect to an external API which has OAuth 2.0 implemented.
The API Store uses a custom version of a grant type called a Client Certificate.
This grant type uses a combination of Mutual SSL and Application level credentials.
It requires two identity factors:
Identity Factor 1 – Mutual SSL: Certificate created by me signed by the API store owner
Identity Factor 2 – Application Level Credentials: {consumerKey:consumerSecret}
The curl command for obtaining this token is:
curl -k -d "grant_type=client_cert" --basic -u "{consumer key}:{consumer secret}" -H "Content-Type: application/x-www-form-urlencoded" --cert {Certificate Pem} https://api.examplestore.com/token
How can I translate this to my Spring boot application?
I've currently written this piece of code, but I think I'm far off.
public void TokenRequest() {
ResponseEntity<String> response = null;
RestTemplate restTemplate = new RestTemplate();
String credentials = String.format("%s:%s", consumerKey, consumerSecret);
String encodedCredentials = new String(Base64.getEncoder().encodeToString(credentials.getBytes()));
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
//headers.setCertificate??
headers.add("Authorization", "Basic " + encodedCredentials);
HttpEntity<String> request = new HttpEntity<String>(headers);
response = restTemplate.exchange(tokenUrl, HttpMethod.POST, request, String.class);
}
Any help is welcome. Thank you :)
I think you are not that far off.
You defenitely need to include the body:
HttpEntity<String> request = new HttpEntity<String>("grant_type=client_cert", headers);
Also you need to include the certificate, maybe like this:
SSLContext sslContext = SSLContextBuilder.create()
.loadTrustMaterial(new URL("/path/to/your/cert"), "certpassword".toCharArray())
.setProtocol("yourProtocol")
.build();
final HttpClient httpClient = HttpClientBuilder.create()
.setSSLContext(sslContext)
.build();
final ClientHttpRequestFactory requestFactory =
new HttpComponentsClientHttpRequestFactory(httpClient);
RestTemplate restTemplate = new RestTemplate(requestFactory);
...
Related
I am using Spring WebClient to hit a Rest Service which requires NTLM Authentication. It works in Postman like below:
Hit the URL - http://example.com:83/api/auth/token with authentication as NTLM authentication and provide the user name and password. When hitting this service, it returns a token.
This token has to be passed in header as bearer token for the actual post service -
http://example.com:89/api/v1/employee
But when I tried the same using Spring WebClient, I am facing 401 - Unauthorized error. Below the code snippet I am using.
BasicCredentialsProvider tokenProvider = new BasicCredentialsProvider();
tokenProvider.setCredentials(
new AuthScope("http", "example.com", 83, "/api/auth/token", StandardAuthScheme.NTLM),
new NTCredentials("testuser", "pwd".toCharArray(), null, null)
);
webClient = WebClient.builder()
.clientConnector(new HttpComponentsClientHttpConnector
(HttpAsyncClients
.custom()
.setDefaultCredentialsProvider(tokenProvider)
.setTargetAuthenticationStrategy(DefaultAuthenticationStrategy.INSTANCE)
.setDefaultRequestConfig(
RequestConfig.custom()
.setAuthenticationEnabled(true)
.setTargetPreferredAuthSchemes(Collections.singletonList(StandardAuthScheme.NTLM))
.setExpectContinueEnabled(true)
.build())
.build()))
.build();
ParameterizedTypeReference<LinkedHashMap<String, Object>> result =
new ParameterizedTypeReference<LinkedHashMap<String, Object>>() {};
Map<String, Object> body = new HashMap<>();
body.put("test-key", "value");
webClient.post().uri("http://example.com:89/api/v1/employee").contentType(MediaType.APPLICATION_JSON).accept(MediaType.ALL).bodyValue(body).retrieve().bodyToMono(result).block();
Is this right approach?
curl -u "uname:password" -H "X-Requested-With: Curl" -X "POST" "https://qualysapi.qg2.apps.qualys.eu/qps/rest/3.0/search/was/wasscan/" > was_finding.txt
How to do the same thing in Spring rest template,
curl -u "uname:password" -H "X-Requested-With: Curl" -X "POST" "https://qualysapi.qg2.apps.qualys.eu/qps/rest/3.0/search/was/wasscan/" > was_finding.txt
Well, I'm not sure why you have the same request two times. Anyhow, you need to initiate a rest client, that you can reuse, and then use it is as a simple http client for any calls:
// Do once
HttpClient client = HttpClients.createDefault();
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(client);
RestTemplate restTemplate = new RestTemplate(new BufferingClientHttpRequestFactory(factory));
// Do anytime
byte[] plainCredsBytes = "uname:password".getBytes();
byte[] base64CredsBytes = Base64.encodeBase64(plainCredsBytes);
String base64Creds = new String(base64CredsBytes);
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Basic " + base64Creds);
ResponseEntity<String> response = restTemplate.postForEntity(
"https://qualysapi.qg2.apps.qualys.eu/qps/rest/3.0/search/was/wasscan/",
new HttpEntity(headers), String.class);
if (response.getStatusCode() == HttpStatus.OK) {
Files.writeString(Paths.get("was_finding.txt"), response.getBody());
} else {
// Handle status code, etc.
}
If you need to set up SSL then you need to create a SSLContextBuilder before you build your client, thats it.
I'm attempting to re-create a REST call I use in Ready-API from java but having issues.
If I make a GET request in ReadyAPI with and I use the AUTH tab in the UI, and set it to Basic, with a username and password and I check "USE GLOBAL PREFERENCE" it works without issue. However if I select "Authenticate pre-emptively" it fails.
Also in readyAPI if I insert an Authorization header with the base64 encoded string, instead of using the "Auth" tab, it also fails. This works for other servers I attempt to talk to, but not this one.
I'm trying to find out why it fails with the Authorization Header. As I'm attempting to make the same call from java with restTemplate.
Something like:
String plainCreds = username + ":" + password;
byte[] plainCredsBytes = StringUtils.getBytesUtf8(plainCreds);
String base64Creds = Base64.encodeBase64String(plainCredsBytes);
httpHeaders = new HttpHeaders();
httpHeaders.add(HttpHeaders.AUTHORIZATION, "Basic " + base64Creds);
What is ReadyAPI doing differently when using the Auth Tab with "Use Global Preferences" that makes it succeed? How can I do this in Java?
The authentication scheme for "basic" needs to be passed with the appropriate capitalization of the scheme name:
httpHeaders.add(HttpHeaders.AUTHORIZATION, "Basic " + base64Creds);
https://www.rfc-editor.org/rfc/rfc7617 Section 2. The 'Basic' Authentication Scheme
I know this is an old question, but I hope this helps somebody.
I had this same scenario and I found the solution here: https://www.baeldung.com/resttemplate-digest-authentication
Basically, you have to create your own RestTemplate bean so the Authorization is by Digest and not Basic:
#Bean(name = "myRestTemplate")
public RestTemplate myRestTemplate(
final String username,
final String password) {
CloseableHttpClient client = HttpClientBuilder.create().
setDefaultCredentialsProvider(provider(username,password)).useSystemProperties().build();
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(client);
return new RestTemplate(requestFactory);
}
private CredentialsProvider provider(String username, String password) {
CredentialsProvider provider = new BasicCredentialsProvider();
UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(username, password);
provider.setCredentials(AuthScope.ANY, credentials);
return provider;
}
Then when you want to use the bean
private String getQueryOutput(String query) {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
ResponseEntity<String> resp = restTemplate.exchange(
"https://myURL/to/accept/post",
HttpMethod.POST,
new HttpEntity<>(query, httpHeaders),
String.class);
return resp.getBody();
}
I am trying to consume a SOAP service with NTLM authentication by creating a NTLM engine (following instructions on http://hc.apache.org/httpcomponents-client-4.3.x/ntlm.html ) implemented AuthSchemeFactory and finally registered the AuthSchemeFactory to my HTTP Client. When I hit the service using my HTTP Client I get a reponse that "Status code - 415 , Message - The server cannot service the request because the media type is unsupported."
Can anybody tell how can I fix this issue of unsupported media to consume a NTLM-protected SOAP web service on Java platform. Is using JCIFS a correct option to conmsume NTLM protected service or are there any better approach(s). Thanks in advance.
DefaultHttpClient httpclient = new DefaultHttpClient();
httpclient.getAuthSchemes().register(AuthSchemes.NTLM,
new JCIFSNTLMSchemeFactory());
CredentialsProvider credsProvider = new BasicCredentialsProvider();
NTCredentials ntcred = new NTCredentials("USERNAME", "PASSWORD",
"HOST", "DOMAIN");
credsProvider.setCredentials(new AuthScope("HOST", 443,
AuthScope.ANY_REALM, "NTLM"), ntcred);
httpclient.setCredentialsProvider(credsProvider);
httpclient.getParams().setParameter(
CoreProtocolPNames.HTTP_CONTENT_CHARSET, "UTF-8");
Writer writer = new StringWriter();
writer.write("MY SOAP REQUEST BODY");
HttpPost httppost = new HttpPost(
"https://<HOST_NAME>/XiPay30WS.asmx");
httppost.setEntity(new StringEntity(writer.toString()));
httppost.setHeader("Content-Type",
"application/x-www-form-urlencoded");
HttpResponse httpresponse = httpclient.execute(
new HttpHost("HOST", 443, "https"),
httppost, new BasicHttpContext());
String statusCode = httpresponse.getStatusCode();
If you use Spring WS support:
Check this Solution
http://dolszewski.com/spring/sharepoint-web-services-spring-and-ntlm-authentication/
#Bean("navisionMessageSender")
public HttpComponentsMessageSender httpComponentsMessageSender() {
HttpComponentsMessageSender httpComponentsMessageSender = new HttpComponentsMessageSender();
String user = env.getProperty("navision.endpoint.user");
String password = env.getProperty("navision.endpoint.password");
String domain = env.getProperty("navision.endpoint.domain");
NTCredentials credentials = new NTCredentials(user, String.valueOf(password), null, domain);
httpComponentsMessageSender.setCredentials(credentials);
return httpComponentsMessageSender;
}
Sample python implementation with NTLM Auth with FLASK.
If you want to use with java , run the standalone flask code below and call the url (e.g POST request /dora/httpWithNTLM ) from java code by http request
from flask import Flask, render_template, flash, request, url_for, redirect, session , Response
import requests,sys,json
from requests_ntlm import HttpNtlmAuth
app = Flask(__name__)
#app.route("/dora/httpWithNTLM",methods=['POST'])
def invokeHTTPReqWithNTLM():
url =""
reqData = json.loads(request.data)
reqxml=request.data
headers = {}
headers["SOAPAction"] = "";
headers["Content-Type"] = "text/xml"
headers["Accept"] = "text/xml"
print("req headers "+str(request.headers))
r = requests.Request("POST",url,auth=HttpNtlmAuth('domain\\username','password'), data=reqxml, headers=headers)
prepared = r.prepare()
s = requests.Session()
resp = s.send(prepared)
print (resp.status_code)
return Response(resp.text.replace("<","<").replace(">",">"),resp.status_code)
if __name__ == '__main__':
app.run(host="0.0.0.0",port=5001)
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());