Android: NTLM Authentication, ksoap, and persistent connections - java

After working with iOS and dealing with auth challenges without much of a learning curve, I've found that Windows Authentication is much more complicated of a process in Java/Android.
I tried multiple different approaches, so without getting too much into those, I will get to the one that worked for the most part. I'm now using the class created for NTLM and ksoap called NtlmTransport
I'm now successfully authenticating in the following way:
NtlmTransport httpTransport = new NtlmTransport();
httpTransport.setCredentials(serverURL, Login.username, Login.password, deviceIp, "DOMAINNAME");
httpTransport.call(SOAP_ACTION, envelope);
If you take a look at the NtlmTransport class, you'll see that it's returning the following headers from the setupNtlm():
status Line HTTP/1.1 200 OK
Setup Cache-Control:private, max-age=0
Setup Content-Type:text/html; charset=utf-8
Setup Server:Microsoft-IIS/8.0
Setup X-AspNet-Version:4.0.30319
Setup Persistent-Auth:true
Setup X-Powered-By:ASP.NET
Setup Date:Tue, 17 Sep 2013 20:57:45 GMT
Setup Content-Length:11549
The "Persistent-Auth:true is the main one I'm concerned about at this time. I'm getting the SoapObjects just fine and can get the data I need from that one connection, but as soon as I try to access the web service again, which is presumably able to be hit after the successful authentication, I can't access a different method using HttpTransportSE:
private void setSomething() {
xml = null;
final String SOAP_ACTION = "http://this.ismy.org/AWebServiceMethod";
final String METHOD_NAME = "AWebServiceMethod";
final String URL = protocol + "://" + host + ":" + port + "/WebService.asmx";
SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME);
SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
envelope.dotNet = true;
envelope.setOutputSoapObject(request);
envelope.implicitTypes = true;
envelope.setAddAdornments(false);
try
{
HttpTransportSE transport = new HttpTransportSE(URL);
transport.debug = true;
transport.call(SOAP_ACTION, envelope);
xml = transport.responseDump.toString();
Log.d(TAG, xml);
}
catch(SocketException ex)
{
Log.e("SocketException : " , "Error on setSomething() " + ex.getMessage());
}
catch (Exception e)
{
Log.e("Exception : " , "Error on setSomething() " + e.getMessage());
}
}
This all works just fine as the background task of an AsyncTask, which then passes the "xml" to an XMLPullParser method.
The main question here is why am I getting a:
Error on setSomething() No authentication challenges found
??
After IIS successfully validates the user with a 200, why is it asking me to authenticate again? How can I persist that first authenticated challenge to hit whatever method I want inside WebService.asmx? What are the headers that need to be added/changed to create a session if necessary? What am I missing that makes this whole NTLM process work and persist for more than the WS method that needs to pass the authentication challenges?
EDIT : Adding the Library code
Here's the link to the JCIFS from Apache
public static final class JCIFSEngine implements NTLMEngine {
private static final int TYPE_1_FLAGS =
NtlmFlags.NTLMSSP_NEGOTIATE_56 |
NtlmFlags.NTLMSSP_NEGOTIATE_128 |
NtlmFlags.NTLMSSP_NEGOTIATE_NTLM2 |
NtlmFlags.NTLMSSP_NEGOTIATE_ALWAYS_SIGN |
NtlmFlags.NTLMSSP_REQUEST_TARGET;
public String generateType1Msg(final String domain, final String workstation)
throws NTLMEngineException {
final Type1Message type1Message = new Type1Message(TYPE_1_FLAGS, domain, workstation);
return jcifs.util.Base64.encode(type1Message.toByteArray());
}
public String generateType3Msg(final String username, final String password,
final String domain, final String workstation, final String challenge)
throws NTLMEngineException {
Type2Message type2Message;
try {
type2Message = new Type2Message(jcifs.util.Base64.decode(challenge));
} catch (final IOException exception) {
throw new NTLMEngineException("Invalid NTLM type 2 message", exception);
}
final int type2Flags = type2Message.getFlags();
final int type3Flags = type2Flags
& (0xffffffff ^ (NtlmFlags.NTLMSSP_TARGET_TYPE_DOMAIN | NtlmFlags.NTLMSSP_TARGET_TYPE_SERVER));
final Type3Message type3Message = new Type3Message(type2Message, Login.password, "",
Login.username, deviceIp, type3Flags);
System.out.println("type3Message: " + type3Message.toByteArray());
return jcifs.util.Base64.encode(type3Message.toByteArray());
}
}
So is the "NtlmFlags.NTLMSSP_NEGOTIATE_ALWAYS_SIGN" causing this problem? Is there another flag I'm supposed to set for the keep-alive? Also, I found a great resource for a list of NTLM flags and more: http://fossies.org/dox/jcifs-1.3.17/interfacejcifs_1_1ntlmssp_1_1NtlmFlags.html

I was also struggling about windows authentication from Android.
I found android-ntlm-master on https://github.com/masconsult/android-ntlm. Add this class as library in your project.
Change is in NtlmTransport.java class.I made change in call method of NtlmTransport class =>
public List call(String soapAction, SoapEnvelope envelope,
List headers, File outputFile)
throws IOException, XmlPullParserException {
HttpResponse resp = null;
try {
//setupNtlm(urlString, user, password);
DefaultHttpClient httpclient = new DefaultHttpClient();
httpclient.getAuthSchemes().register("ntlm", new NTLMSchemeFactory());
httpclient.getCredentialsProvider().setCredentials(
new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT),
new NTCredentials(user, password, "", "")
);
HttpPost httpget = new HttpPost(urlString);
httpget.addHeader("soapaction", soapAction);
httpget.addHeader("Content-Type", "text/xml; charset=utf-8");
byte[] requestData = null;
try {
requestData = createRequestData(envelope);
} catch (IOException iOException) {
}
ByteArrayEntity byteArrayEntity = new ByteArrayEntity(requestData);
httpget.setEntity(byteArrayEntity);
resp = httpclient.execute(httpget);
if(resp == null) {
System.out.println("Response is null");
}
HttpEntity respEntity = resp.getEntity();
InputStream is = respEntity.getContent();
if(is == null) {
System.out.println("InputStream is null");
}
parseResponse(envelope, is);
} catch (Exception ex) {
// ex.printStackTrace();
}
if (resp != null) {
return Arrays.asList(resp.getAllHeaders());
} else {
return null;
}
}
And below is the code how I make call:
SoapObject request = new SoapObject(NAMESPACE, PRODUCT_DETAILS_METHOD_NAME);
request.addProperty("ListingID", Integer.parseInt(Product_ID));
NtlmTransport httpTransport = new NtlmTransport();
httpTransport.setCredentials(URL, USERNAME, PASSWORD, "","");
SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
envelope.dotNet = true;
envelope.implicitTypes = true;
envelope.setOutputSoapObject(request);
httpTransport.call(PRODUCT_DETAILS_SOAP_ACTION, envelope);
SoapObject response = (SoapObject) envelope.getResponse();
It worked for me.
More you can find here: https://suhas1989.wordpress.com/2015/01/28/ntlm-authentication-in-android/

Related

Send JSON body in HTTP GET request in java/spring boot

I need to send a GET request with a json body in java/spring boot. I'm aware of the advice against it, however I have to do it this was for a couple of reasons:
1. The 3rd party API I'm using only allows GET requests, so POST is not an option.
2. I need to pass an extremely large parameter in the body (a comma separated list of about 8-10k characters) so tacking query params onto the url is not an option either.
I've tried a few different things:
apache HttpClient from here: Send content body with HTTP GET Request in Java. This gave some error straight from the API itself about a bad key.
URIComponentsBuilder from here: Spring RestTemplate GET with parameters. This just tacked the params onto the url, which as I explained before is not an option.
restTemplate.exchange. This seemed the most straightforward, but the object wouldn't pass: https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html#exchange-java.lang.String-org.springframework.http.HttpMethod-org.springframework.http.HttpEntity-java.lang.Class-java.util.Map-
as well as probably another thing or two that I've forgotten about.
Here is what I'm talking about in Postman. I need to be able to pass both of the parameters given here. It works fine if run through Postman, but I can't figure it out in Java/Spring Boot.
Here is a code snippet from the restTemplate.exchange attempt:
public String makeMMSICall(String uri, List<String> MMSIBatchList, HashMap<String, String> headersList) {
ResponseEntity<String> result = null;
try {
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
for (String key : headersList.keySet()) {
headers.add(key, headersList.get(key));
}
Map<String, String> params = new HashMap<String, String>();
params.put("mmsi", String.join(",", MMSIBatchList));
params.put("limit", mmsiBatchSize);
HttpEntity<?> entity = new HttpEntity<>(headers);
result = restTemplate.exchange(uri, HttpMethod.GET, entity, String.class, params);
System.out.println(result.getBody());
} catch (RestClientException e) {
LOGGER.error("Exception in makeGetHTTPCall :" + e.getMessage());
throw e;
} catch (Exception e) {
LOGGER.error("Exception in makeGetHTTPCall :" + e.getMessage());
throw e;
}
return result.getBody();
}
Thanks for helping!
You can try java.net.HttpUrlConnection, it works for me but indeed I normally use a POST
HttpURLConnection connection = null;
BufferedReader reader = null;
String payload = "body";
try {
URL url = new URL("url endpoint");
if (url.getProtocol().equalsIgnoreCase("https")) {
connection = (HttpsURLConnection) url.openConnection();
} else {
connection = (HttpURLConnection) url.openConnection();
}
// Set connection properties
connection.setRequestMethod(method); // get or post
connection.setReadTimeout(3 * 1000);
connection.setDoOutput(true);
connection.setUseCaches(false);
if (payload != null) {
OutputStream os = connection.getOutputStream();
os.write(payload.getBytes(StandardCharsets.UTF_8));
os.flush();
os.close();
}
int responseCode = connection.getResponseCode();
}
There's no way of implementing it via RestTemplate, even with .exchange method. It'll simply not send the request body for GET calls even if we pass the entity within the function parameters.(Tested via interceptor logs)
You can use the Apache client to solve this issue/request (whatever you'd like to call it). The code you need is something along following lines.
private static class HttpGetWithBody extends HttpEntityEnclosingRequestBase {
JSONObject requestBody;
public HttpGetWithBody(URI uri, JSONObject requestBody) throws UnsupportedEncodingException {
this.setURI(uri);
StringEntity stringEntity = new StringEntity(requestBody.toString());
super.setEntity(stringEntity);
this.requestBody = requestBody;
}
#Override
public String getMethod() {
return "GET";
}
}
private JSONObject executeGetRequestWithBody(String host, Object entity) throws ClientProtocolException, IOException {
CloseableHttpClient httpClient = HttpClients.createDefault();
try{
JSONObject requestBody = new JSONObject(entity);
URL url = new URL(host);
HttpRequest request = new HttpGetWithBody(url.toURI(), requestBody);
request.addHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
request.addHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE);
HttpResponse response;
if(url.getPort() != 0) response = httpClient.execute(new HttpHost(url.getHost(), url.getPort()), request);
else response = httpClient.execute(new HttpHost(url.getHost()), request);
if(response.getStatusLine().getStatusCode() == HttpStatus.SC_OK){
JSONObject res = new JSONObject(EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8));
httpClient.close();
return res;
}
}catch (Exception e){
log.error("Error occurred in executeGetRequestWithBody. Error: ", e.getStackTrace());
}
httpClient.close();
return null;
}
If you inspect even Apache client library doesn't support passing the body natively(checked via code implementation of HttpGet method), since contextually request body for a GET request is not a good and obvious practice.
Try creating a new custom RequestFactory.
Similar to
get request with body

Consume REST API, Send data using x-www-form-urlencoded

I am trying to consume a web service to get a token. It is a POST service and I must send data using x-www-form-urlencoded, but I am not sure how to do it. I have the following code, but an error "400 Bad Request" returns. I'm using jersey.api.client and gson. The service returns a JSON object.
public VOToken getToken() {
String uri = "https://login.mypurecloud.com/oauth/token";
VOToken voToken = null;
ClientConfig clientConfig = new DefaultClientConfig();
clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE);
System.out.println(getAuthorizationHeaderString());
Client client = Client.create(clientConfig);
WebResource webResource = client.resource(uri);
Form form = new Form();
form.add("grant_type", "client_credentials");
WebResource.Builder builder = webResource.accept(MediaType.APPLICATION_FORM_URLENCODED_TYPE);
builder.header("Authorization", getAuthorizationHeaderString());
builder.entity(form);
//Response
ClientResponse clientResponse = builder.type(MediaType.APPLICATION_JSON).post(ClientResponse.class);
clientResponse.bufferEntity();
String jsonString = clientResponse.getEntity(String.class);
if(clientResponse.getStatus() == 200 ) {
voToken = new Gson().fromJson(jsonString, VOToken.class);
System.out.println(">> Access_token: "+ voToken.getAccess_token());
}
return voToken;
}
public String getAuthorizationHeaderString() {
String clientId = "32ef8d9c-######################";
String clientSecret = "6-M5A8Y06##################";
String authorizationHeaderString = "";
try {
String encodedData = DatatypeConverter.printBase64Binary((clientId + ":" + clientSecret).getBytes("UTF-8"));
authorizationHeaderString = "Basic " + encodedData;
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return authorizationHeaderString;
}
I believe it is APPLICATION_FORM_URLENCODED and not APPLICATION_FORM_URLENCODED_TYPE.
Also in your ClientResponse change the media type to APPLICATION_FORM_URLENCODED

Jersey JAVA REST Client giving Error 500 "BAD Request" for POST request, while POSTMAN is able POST to same Restful API

I am trying to post form data through a JAVA Jersey REST client but i receive the response code 500 and an according exception:
java.lang.RuntimeException: Failed with HTTP error code : 500
The same request from POSTMAN(Chrome Extention) works successfully.
I am making a POST request to StreamSets Data Collector API.
Below is my Code
public static String testUploadService(String httpURL, File filePath) throws Exception {
// local variables
ClientConfig clientConfig = null;
Client client = null;
WebTarget webTarget = null;
Invocation.Builder invocationBuilder = null;
Response response = null;
FileDataBodyPart fileDataBodyPart = null;
FormDataMultiPart formDataMultiPart = null;
int responseCode;
String responseMessageFromServer = null;
String responseString = null;
String name = "*******";
String password = "*******";
String authString = name + ":" + password;
String sdc="sdc";
byte[] encoding = Base64.getEncoder().encode(authString.getBytes());
byte[] encoding2 = Base64.getEncoder().encode(sdc.getBytes());
String USER_PASS = new String(encoding);
String auth2=new String(encoding2);
try{
ClientConfig cc = new ClientConfig();
cc.register(MultiPartFeature.class);
try {
client = new JerseywithSSL().initClient(cc);
} catch (KeyManagementException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
webTarget = client.target(httpURL);
// set file upload values
fileDataBodyPart = new FileDataBodyPart("uploadFile", filePath, MediaType.MULTIPART_FORM_DATA_TYPE);
formDataMultiPart = new FormDataMultiPart();
formDataMultiPart.bodyPart(fileDataBodyPart);
// invoke service
invocationBuilder = webTarget.request();
invocationBuilder.header("Authorization", "Basic " + USER_PASS);
invocationBuilder.header("X-Requested-By","SDC"); //Additional Header requiered by Streamsets RestAPI
invocationBuilder.header("Content-type", "multipart/form-data");
response = invocationBuilder.post(Entity.entity(formDataMultiPart, MediaType.MULTIPART_FORM_DATA));
// get response code
responseCode = response.getStatus();
System.out.println("Response code: " + responseCode);
if (response.getStatus() != 200) {
throw new RuntimeException("Failed with HTTP error code : " + responseCode);
}
// get response message
responseMessageFromServer = response.getStatusInfo().getReasonPhrase();
System.out.println("ResponseMessageFromServer: " + responseMessageFromServer);
// get response string
responseString = response.readEntity(String.class);
}
catch(Exception ex) {
ex.printStackTrace();
}
finally{
// release resources, if any
fileDataBodyPart.cleanup();
formDataMultiPart.cleanup();
formDataMultiPart.close();
response.close();
client.close();
}
return responseString;
}
}
And here is screenshot of POSTMAN of all header and Authentication included,
I can't figure out whether its an issue with forming a multipart or is it an issue on the server side and if its the former than where exactly am I going wrong?
PS: I got over SSL certificate error by adding Trust certificate.
UPDATE 1
After I dig further into I got Following error stacktrace.
responseString : {
"RemoteException" : {
"message" : "java.lang.NullPointerException: in is null",
"errorCode" : "CONTAINER_0000",
"localizedMessage" : "in is null",
"exception" : "NullPointerException",
"javaClassName" : "java.lang.NullPointerException",
"stackTrace" : "java.lang.NullPointerException: in is null\n\tat java.util.zip.ZipInputStream.<init>(ZipInputStream.java:101)\n\tat java.util.zip.ZipInputStream.<init>(ZipInputStream.java:80)\n\tat com.streamsets.datacollector.restapi.PipelineStoreResource.importPipelines(PipelineStoreResource.java:551)\n\tat sun.reflect.GeneratedMethodAccessor573.invoke(Unknown Source)\n\tat sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\n\tat java.lang.reflect.Method.invoke(Method.java:498)\n\tat org.glassfish.jersey.server.model.internal.ResourceMethodInvocationHandlerFactory$1.invoke(ResourceMethodInvocationHandlerFactory.java:81)\n\tat org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher$1.run(AbstractJavaResourceMethodDispatcher.java:144)\n\tat org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.invoke(AbstractJavaResourceMethodDispatcher.java:161)\n\tat org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$ResponseOutInvoker.doDispatch(JavaResourceMethodDispatcherProvider.java:160)\n\tat org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.dispatch(AbstractJavaResourceMethodDispatcher.java:99)\n\tat org.glassfish.jersey.server.model.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:389)\n\tat org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:347)\n\tat org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:102)\n\tat org.glassfish.jersey.server.ServerRuntime$2.run(ServerRuntime.java:326)\n\tat org.glassfish.jersey.internal.Errors$1.call(Errors.java:271)\n\tat org.glassfish.jersey.internal.Errors$1.call(Errors.java:267)\n\tat org.glassfish.jersey.internal.Errors.process(Errors.java:315)\n\tat org.glassfish.jersey.internal.Errors.process(Errors.java:297)\n\tat org.glassfish.jersey.internal.Errors.process(Errors.java:267)\n\tat org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:317)\n\tat org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:305)\n\tat org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:1154)\n\tat org.glassfish.jersey.servlet.WebComponent.serviceImpl(WebComponent.java:473)\n\tat org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:427)\n\tat org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:388)\n\tat org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:341)\n\tat org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:228)\n\tat org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:841)\n\tat org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1634)\n\tat com.streamsets.datacollector.http.GroupsInScopeFilter.lambda$doFilter$0(GroupsInScopeFilter.java:82)\n\tat com.streamsets.datacollector.security.GroupsInScope.execute(GroupsInScope.java:33)\n\tat com.streamsets.datacollector.http.GroupsInScopeFilter.doFilter(GroupsInScopeFilter.java:81)\n\tat org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1621)\n\tat org.eclipse.jetty.servlets.CrossOriginFilter.handle(CrossOriginFilter.java:308)\n\tat org.eclipse.jetty.servlets.CrossOriginFilter.doFilter(CrossOriginFilter.java:262)\n\tat org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1621)\n\tat com.streamsets.datacollector.http.LocaleDetectorFilter.doFilter(LocaleDetectorFilter.java:39)\n\tat org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1621)\n\tat com.streamsets.pipeline.http.MDCFilter.doFilter(MDCFilter.java:47)\n\tat org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1621)\n\tat org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:541)\n\tat org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143)\n\tat org.eclipse.jetty.server.handler.gzip.GzipHandler.handle(GzipHandler.java:494)\n\tat org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:513)\n\tat org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132)\n\tat org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:190)\n\tat org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1592)\n\tat org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:188)\n\tat org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1239)\n\tat org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:168)\n\tat org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:481)\n\tat org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1561)\n\tat org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:166)\n\tat org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1141)\n\tat org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)\n\tat org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132)\n\tat org.eclipse.jetty.rewrite.handler.RewriteHandler.handle(RewriteHandler.java:335)\n\tat org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:118)\n\tat org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:213)\n\tat org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132)\n\tat org.eclipse.jetty.server.Server.handle(Server.java:564)\n\tat org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:320)\n\tat org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:251)\n\tat org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:279)\n\tat org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:110)\n\tat org.eclipse.jetty.io.ssl.SslConnection.onFillable(SslConnection.java:258)\n\tat org.eclipse.jetty.io.ssl.SslConnection$3.succeeded(SslConnection.java:147)\n\tat org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:110)\n\tat org.eclipse.jetty.io.ChannelEndPoint$2.run(ChannelEndPoint.java:124)\n\tat org.eclipse.jetty.util.thread.Invocable.invokePreferred(Invocable.java:122)\n\tat org.eclipse.jetty.util.thread.strategy.ExecutingExecutionStrategy.invoke(ExecutingExecutionStrategy.java:58)\n\tat org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.produceConsume(ExecuteProduceConsume.java:201)\n\tat org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.run(ExecuteProduceConsume.java:133)\n\tat org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:672)\n\tat org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:590)\n\tat java.lang.Thread.run(Thread.java:748)\n"
}
}

How to develop a spring rest web service in accessing external API?

I want to develop the spring rest web service which accesses the external API when user sends request. User sends a json request and set of request headers to the web service and web service should authenticate the user and call the eternal API. External API response should be given to the user as the response body. This is what I want to happen basically.
//Authenticate to access API
public class HotelbedsAuthentication {
final String hotelEndpoint = "https://api.test.hotelbeds.com/hotel-api/1.0/";
private String request;
private static final String apiKey="enter given api key for free";
private static final String secretKey="free key";
private String signature=org.apache.commons.codec.digest.DigestUtils.sha256Hex(apiKey + secretKey + System.currentTimeMillis() / 1000);
public HttpsURLConnection findHotels(){
HttpsURLConnection connection = null;
try{
URL url = new URL(hotelEndpoint+getRequest());
HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
con.setRequestProperty("X-Signature", signature);
con.setRequestProperty("Api-Key", apiKey);
con.setRequestProperty("Accept", "application/json");
con.setRequestProperty("Accept-Encoding", "gzip");
con.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
con.setRequestMethod("POST");
con.setDoOutput(true);
con.setDoInput (true);
connection=con;
}catch(Exception error ){
System.out.println("An error occured "+error);
}
return connection;
}
public String getRequest() {
return request;
}
public void setRequest(String request) {
this.request = request;
}
}
//Rest Controller
public class FindHotelController {
HotelbedsAuthentication token = new HotelbedsAuthentication();
#RequestMapping(value="hotels",method=RequestMethod.POST,produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public HotelAvailability findHotel(#RequestBody JSONObject request){
HttpsURLConnection connection;
File pathDir = new File("C:/Users/User/workspace/SpringRestSample/src/main/java");
JCodeModel codeModel = new JCodeModel();
JSONObject response= new JSONObject();
HotelAvailability sample= new HotelAvailability();
//String userRequest="{\"stay\": {\"checkIn\": \"2018-01-29\",\"checkOut\": \"2018-01-31\",\"shiftDays\": \"2\"},\"occupancies\": [{\"rooms\": 1,\"adults\": 2,\"children\": 1,\"paxes\": [{\"type\": \"AD\",\"age\": 30},{\"type\": \"AD\",\"age\": 30},{\"type\": \"CH\",\"age\": 8}]}],\"hotels\": {\"hotel\": [1067,1070,1075,135813,145214,1506,1508,1526,1533,1539,1550,161032,170542,182125,187939,212167,215417,228671,229318,23476]}}";
try{
token.setRequest("hotels");
connection= token.findHotels();
//Read request and embed to url
OutputStream os = connection.getOutputStream();
os.write(request.toString().getBytes("UTF-8"));
os.close();
// read the response
InputStream in = new BufferedInputStream(connection.getInputStream());
String result = org.apache.commons.io.IOUtils.toString(in, "UTF-8");
JSONObject jsonObject = new JSONObject(result);
response=jsonObject;
GenerationConfig config = new DefaultGenerationConfig() {
#Override
public boolean isGenerateBuilders() {
return true;
}
public SourceType getSourceType(){
return SourceType.JSON;
}
};
SchemaMapper mapper =new SchemaMapper(new RuleFactory(config, new GsonAnnotator(config), new SchemaStore()), new SchemaGenerator());
mapper.generate(codeModel, "HotelAvailability","com.sample.model",response.toString());
codeModel.build(pathDir);
ObjectMapper objectMapper = new ObjectMapper();
sample = objectMapper.readValue(response.toString(), HotelAvailability.class);
in.close();
connection.disconnect();
}catch(Exception ex){
System.out.println(ex);
}
System.out.println(sample);
return sample;
}
}
What you're looking for is an API Gateway or a simple router based on your requirements. If you need to make changes to the response, you need a Gateway. If you're looking to simply pass the request then you need a router.
There are many ways to do this, but as always, Spring has already built a tool for that. check out Zuul. This will allow you to integrate with an Authentication provider and then delegate requests to microservices.
Gateway
https://www.intertech.com/Blog/spring-integration-tutorial-part-8-gateways/
OAuth Provider
https://spring.io/guides/tutorials/spring-boot-oauth2/#_social_login_authserver
Router
https://cloud.spring.io/spring-cloud-netflix/multi/multi__router_and_filter_zuul.html

How do I upload a document to SharePoint with Java?

I'm creating some big files (DB exports) with Java and I need to put them somewhere on our SharePoint server. Right now, I'm doing this with IE but I'd like to automate this step, too.
I searched the web and I found some hints to use SOAP but I don't really see to the ground of all this, yet. Can someone provide me with some sample code or a recipe what I need to do?
Please note: the SharePoint server asks for NT domain authentication. I can't even login with Firefox :(
EDIT
How do I convert the scary URL in IE into a WebDAV path?
Is there a WebDAV "explorer" which I can use before I ruin the production system with my code? I tried "DAV Explorer 0.91" from http://www.davexplorer.org/ but that can't connect (probably because of NT domain auth).
In addition to Sacha's suggestions, you can use the SharePoint SOAP web services. Each SharePoint site exposes a bunch of web services via the path http://<Site>/_vti_bin/.
In your case, you probably want the Lists web service (http://<Site>/_vti_bin/Lists.asmx). You can grab the WSDL from http://<Site>/_vti_bin/Lists.asmx?WSDL. The WSS 3.0 SDK has details on how to use the web service (you'll probably want to use the UpdateListItems and AddAttachment methods).
All that said, Sacha's first option (mapping a document library to a drive) is probably the easiest way assuming you can get around the NTLM issues.
If you're using Windows you can simply navigate to a UNC path for a document library. For example, if the browser URL for your document library is:
http://<Site>/Foo/BarDocs/Forms/AllItems.aspx
you can simply type the corresponding UNC path in the Windows Explorer address bar:
\\<Site>\Foo\BarDocs
and then drag and drop files to this location. If you'd like you can map this location to a drive letter using Windows Explorer or the SUBST.EXE command-line utility.
Okay ... after several hours of work and biting myself through the "documentation" MicroSoft provides and all the hints randomly spread over the 'net, I've managed to write some sample code to browse the content of a SharePoint server: Navigating SharePoint Folders With Axis2.
Next stop: Uploading something.
An other solution is to use HTTP PUT method to send a file directly to the Sharepoint.
For that you can use Apache HTTP Client:
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.2.3</version>
</dependency>
And to permit NTLMv2 authentication you need JCIF library.
<dependency>
<groupId>jcifs</groupId>
<artifactId>jcifs</artifactId>
<version>1.3.17</version>
</dependency>
First we need to write a wrapper to permit Apache HTTP Client to use JCIF for NTLMv2 support :
public final class JCIFSEngine implements NTLMEngine {
private static final int TYPE_1_FLAGS =
NtlmFlags.NTLMSSP_NEGOTIATE_56
| NtlmFlags.NTLMSSP_NEGOTIATE_128
| NtlmFlags.NTLMSSP_NEGOTIATE_NTLM2
| NtlmFlags.NTLMSSP_NEGOTIATE_ALWAYS_SIGN
| NtlmFlags.NTLMSSP_REQUEST_TARGET;
#Override
public String generateType1Msg(final String domain, final String workstation)
throws NTLMEngineException {
final Type1Message type1Message = new Type1Message(TYPE_1_FLAGS, domain, workstation);
return Base64.encode(type1Message.toByteArray());
}
#Override
public String generateType3Msg(final String username, final String password,
final String domain, final String workstation, final String challenge)
throws NTLMEngineException {
Type2Message type2Message;
try {
type2Message = new Type2Message(Base64.decode(challenge));
} catch (final IOException exception) {
throw new NTLMEngineException("Invalid NTLM type 2 message", exception);
}
final int type2Flags = type2Message.getFlags();
final int type3Flags = type2Flags
& (0xffffffff ^ (NtlmFlags.NTLMSSP_TARGET_TYPE_DOMAIN | NtlmFlags.NTLMSSP_TARGET_TYPE_SERVER));
final Type3Message type3Message = new Type3Message(type2Message, password, domain,
username, workstation, type3Flags);
return Base64.encode(type3Message.toByteArray());
}
}
Reference
The main code to execute HTTP PUT with authentication:
try {
HttpParams params = new BasicHttpParams();
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
DefaultHttpClient httpclient = new DefaultHttpClient(params);
//Register JCIF NTLMv2 to manage ntlm auth.
httpclient.getAuthSchemes().register("ntlm", new AuthSchemeFactory() {
#Override
public AuthScheme newInstance(HttpParams hp) {
return new NTLMScheme(new JCIFSEngine());
}
});
//Provide login/password
httpclient.getCredentialsProvider().setCredentials(
AuthScope.ANY,
new NTCredentials([LOGIN], [PASSWORD], "", [DOMAIN]));
//Create HTTP PUT Request
HttpPut request = new HttpPut("http://[server]/[site]/[folder]/[fileName]");
request.setEntity(new FileEntity([File]));
return httpclient.execute(request);
} catch (IOException ex) {
//...
}
I can think of different options:
Mapping the Document library to a file drive and just save the file like any other file in the file system.
Using HTTP WebDAV protocol.
...and for the NTLM authentication part:
http://www.luigidragone.com/networking/ntlm.html
I think my approach might help you.
Initially i have created sharepoint account and followed the procedure in this link (http://www.ktskumar.com/2017/01/access-sharepoint-online-using-postman/) to get needed credentials for REST API's. once i got the credentials all i needed was the following dependency and code:
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5</version>
</dependency>
Since i used OAUTH2 authentication, the code to get access token helps for other CRUD operations.
/* OAuth2 authentication to get access token */
public String getSharePointAccessToken() throws ClientProtocolException, IOException
{
/* Initializing variables */
String grant_type = RcConstants.GRANT_TYPE;
String client_id = RcConstants.CLIENT_ID;
String client_secret = RcConstants.CLIENT_SECRET;
String resource = RcConstants.RESOURCE;
String url = RcConstants.OAUTH_URL + RcConstants.URL_PARAMETER + "/tokens/OAuth/2";
/*
* NOTE: RcConstants.OAUTH_URL =
* https://accounts.accesscontrol.windows.net/ RcConstants.URL_PARAMETER
* = Bearer Realm from
* (http://www.ktskumar.com/2017/01/access-sharepoint-online-using-
* postman/) Figure 6.
*/
/* Building URL */
HttpClient client = HttpClientBuilder.create().build();
HttpPost post = new HttpPost(url);
post.setHeader("Content-Type", "application/x-www-form-urlencoded");
/* Adding URL Parameters */
List<NameValuePair> urlParameters = new ArrayList<NameValuePair>();
urlParameters.add(new BasicNameValuePair("grant_type", grant_type));
urlParameters.add(new BasicNameValuePair("client_id", client_id));
urlParameters.add(new BasicNameValuePair("client_secret", client_secret));
urlParameters.add(new BasicNameValuePair("resource", resource));
post.setEntity(new UrlEncodedFormEntity(urlParameters));
/* Executing the post request */
HttpResponse response = client.execute(post);
logger.debug("Response Code : " + response.getStatusLine().getStatusCode());
String json_string = EntityUtils.toString(response.getEntity());
JSONObject temp1 = new JSONObject(json_string);
if (temp1 != null)
{
/* Returning access token */
return temp1.get("access_token").toString();
}
return RcConstants.OAUTH_FAIL_MESSAGE;
}
Once we get access token we can upload using following method:
public String putRecordInSharePoint(File file) throws ClientProtocolException, IOException
{
/* Token variable declaration */
String token = getSharePointAccessToken();
/* Null or fail check */
if (!token.equalsIgnoreCase(RcConstants.OAUTH_FAIL_MESSAGE))
{
/* Upload path and file name declaration */
String Url_parameter = "Add(url='" + file.getName() + "',overwrite=true)";
String url = RcConstants.UPLOAD_FOLDER_URL + Url_parameter;
/*
* NOTE: RcConstants.UPLOAD_FOLDER_URL =
* https://<your_domain>.sharepoint.com/_api/web/
* GetFolderByServerRelativeUrl('/Shared%20Documents/<FolderName>')/
* Files/
*/
/* Building URL */
HttpClient client = HttpClientBuilder.create().build();
HttpPost post = new HttpPost(url);
post.setHeader("Authorization", "Bearer " + token);
post.setHeader("accept", "application/json;odata=verbose");
/* Declaring File Entity */
post.setEntity(new FileEntity(file));
/* Executing the post request */
HttpResponse response = client.execute(post);
logger.debug("Response Code : " + response.getStatusLine().getStatusCode());
if (response.getStatusLine().getStatusCode() == HttpStatus.OK.value()|| response.getStatusLine().getStatusCode() == HttpStatus.ACCEPTED.value())
{
/* Returning Success Message */
return RcConstants.UPLOAD_SUCCESS_MESSAGE;
}
else
{
/* Returning Failure Message */
return RcConstants.UPLOAD_FAIL_MESSAGE;
}
}
return token;
}
I managed to up and download files to sharepoint with this code, using the integrated Windows identification, maybe it helps.
public class HttpClient {
HttpClient() { }
public static void download(final String source, final File resultingFile) {
CloseableHttpClient client = WinHttpClients.createSystem();
HttpGet httpRequest = new HttpGet(source);
CloseableHttpResponse httpResponse = null;
try {
httpResponse = client.execute(httpRequest);
HttpEntity entity = httpResponse.getEntity();
if(httpResponse.getStatusLine() != null && httpResponse.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
LOGGER.warn(httpResponse.getStatusLine());
}else {
LOGGER.debug(httpResponse.getStatusLine());
FileUtils.touch(resultingFile);
InputStream is = entity.getContent();
File outFile = new File(resultingFile.getAbsolutePath());
FileOutputStream fos = new FileOutputStream(outFile);
int inByte;
while ((inByte = is.read()) != -1) {
fos.write(inByte);
}
is.close();
fos.close();
client.close();
}
} catch (ClientProtocolException e) {
LOGGER.warn(e);
} catch (UnsupportedOperationException e) {
LOGGER.warn(e);
} catch (IOException e) {
LOGGER.warn(e);
}
}
public static void upload(final File source, final String destination) {
CloseableHttpClient httpclient = WinHttpClients.createSystem();
HttpPut httpRequest = new HttpPut(destination);
httpRequest.setEntity(new FileEntity(new File(source.getPath())));
CloseableHttpResponse httpResponse = null;
try {
httpResponse = httpclient.execute(httpRequest);
EntityUtils.consume(httpResponse.getEntity());
if (httpResponse.getStatusLine() != null && httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_CREATED) {
LOGGER.debug(httpResponse.getStatusLine());
LOGGER.info("Upload of " + source.getName() + " via HTTP-Client succeeded.");
} else if (httpResponse.getStatusLine() != null && httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
LOGGER.debug(httpResponse.getStatusLine());
}else {
LOGGER.warn("Uploading " + source.getName() + " failed.");
LOGGER.warn(httpResponse.getStatusLine().getStatusCode() + ": " + httpResponse.getStatusLine().getReasonPhrase());
}
} catch (IOException e) {
LOGGER.warn(e);
LOGGER.warn(e.getMessage());
}
return;
}
}
WinHttpClients:
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient-win</artifactId>
<version>4.4</version>
</dependency>
Path:
org.apache.http.impl.client.WinHttpClients
Description:
Factory methods for CloseableHttpClient instances configured to use integrated Windows authentication by default.

Categories