I am trying to access a secure .NET SOAP webservice in an Android app, using the KSOAP2 library. I have checked for other answers on Stack Overflow, but so far none of them have helped me.
A username and password are required for authentication. The method also takes a String and returns a complex type. I have gotten this to work with an identical but unsecure service (ie, no username/password, but passing String and returning complex type works fine).
I can connect to the secure webservice using soapUI. All I have to do is give it the correct String parameter, set the username and password through the SoapUI request properties, and also set the WSS-PasswordType to "PasswordText".
I am confused about how to achieve this through my Java code. The SoapUI request (which works) looks like this:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/">
<soapenv:Header><wsse:Security soapenv:mustUnderstand="1" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsse:UsernameToken wsu:Id="UsernameToken-1" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<wsse:Username>(my username) </wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">(my password) </wsse:Password>
<wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">eic5EdyTomcBLocwyph5Mw==</wsse:Nonce>
<wsu:Created>2012-02-08T09:47:55.225Z</wsu:Created>
</wsse:UsernameToken></wsse:Security>
</soapenv:Header>
<soapenv:Body>
<tem:GetClientByNationalID>
<!--Optional:-->
<tem:nationalID>(the nationalID) </tem:nationalID>
</tem:GetClientByNationalID>
</soapenv:Body>
</soapenv:Envelope>
I figured I should be passing username and password in the Soap header then so I tried following this answer but this didn't work. I then used [Lalit's answer] (SOAP web service on android) below to edit the header of my request to try to make it identical to the SoapUI-generated request, but I am still getting the same error. Here is what my code is now producing:
<v:Envelope xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://www.w3.org/2001/XMLSchema" xmlns:c="http://schemas.xmlsoap.org/soap/encoding/" xmlns:v="http://schemas.xmlsoap.org/soap/envelope/">
<v:Header>
<n0:Security mustUnderstand="1" xmlns:n0="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<n0:UsernameToken n1:Id="UsernameToken-1" xmlns:n1="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<n0:Username>(my username)</n0:Username>
<n0:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">(mypassword)</n0:Password>
<n0:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">eic5EdyTomcBLocwyph5Mw==</n0:Nonce>
<wsu:Created>2012-02-08T09:47:55.225Z</wsu:Created>
</n0:UsernameToken>
</n0:Security>
</v:Header>
<v:Body>
<GetClientByNationalID xmlns="http://tempuri.org/" id="o0" c:root="1">
<nationalID i:type="d:string">(testnationalID)</nationalID>
</GetClientByNationalID>
</v:Body>
This is the error I'm still getting in LogCat:
02-10 10:53:49.261: W/System.err(1187): javax.net.ssl.SSLHandshakeException:
java.security.cert.CertPathValidatorException: Trust anchor for certification path
not found.
This makes it look like a certificate issue, so I imported the cert from the server with InstallCert and verified with Keytool that it's in my keystore. This made no difference. On the other hand, I assume it's not a certificate issue since SoapUI worked before I had that cert installed.
Now I'm confused, so here are my questions:
1) Is adding the cert to the cacerts keystore in my JRE lib/security enough, or do I need to write some Java code to then access this cert?
2) Is this a cert issue at all, if SoapUI was able to connect successfully to the webservice without me installing the cert?
3) If it's not a cert issue, can anyone advise on what kind of code I need to use to produce a Soap request that will successfully authenticate and then execute the method call? I presume this is a case of getting my java-produced request to look as similar as possible to the SoapUI-generated request.
You can create the Header element and add the Authentication details,
Element[] header = new Element[1];
header[0] = new Element().createElement(NAMESPACE,"AuthHeader");
Add Username tag with username,
Element username = new Element().createElement(NAMESPACE, "username_tag");
username.addChild(Node.TEXT, "username_value");
header[0].addChild(Node.ELEMENT, username);
Add Password tag with password,
Element password = new Element().createElement(NAMESPACE, "password_tag");
password.addChild(Node.TEXT, "password_value");
header[0].addChild(Node.ELEMENT, password);
Add the header to the SoapSerializationEnvelope
SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope
.VER11);
envelope.dotNet = true;
envelope.headerOut = header;
envelope.setOutputSoapObject(request);
I hope it is not too late. Please have a look at the answer on the following link.
https://stackoverflow.com/a/17691465/2499764
I can share the code for constructing the header if you are still interested.
Related
I am trying to create soap request in android but not able to get how to generate this.
I have gone through following URL:-
url-1
url-2
but not able to understand how to create soap request like this.
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:stal="http://ws.soapwebserv.com/Info">
<soapenv:Header>
<wsse:Security soapenv:mustUnderstand="1" xmlns:wsse="http://docs.oasis- open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsse:UsernameToken wsu:Id="UsernameToken-30" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<wsse:Username>alias101#soapwebserv.com</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">somesecurepassword</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
</soapenv:Header>
<soapenv:Body>
<stal:InfoRequest>
<token>randomtokenrandomtoken</token>
</stal:InfoRequest>
</soapenv:Body>
</soapenv:Envelope>
please help me out guys... I am new to Soap. :(
simple way is to copy the entire soapenvelope in raw folder as xml file and pass your username ,password and token with help of pattern matching . Now you have the entire request in the form of string pass the request to httppost method.let me know that helps your issue.
I'm using ksoap2 library on android to connect to a web service. I run the following code:
SoapObject request = new SoapObject(NAMESPACE, method);
request.addProperty("UserName", username);
request.addProperty("TransactionID", id);
SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
nvelope.setOutputSoapObject(request);
HttpTransportSE ht = new HttpTransportSE(URL);
ht.call(NAMESPACE + method, envelope);
As you can see above, there are two parameters. Here's the wsdl file for this part:
<s:element minOccurs="1" maxOccurs="1" name="TransactionID" type="s:int" />
<s:element minOccurs="0" maxOccurs="1" name="UserName" type="s:string" />
Problem is that I get different responses from the time I call it with same parameters from SoapUI. Here's how SoapUI request looks like:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:req="http://whatever.com/Webservice/Request.asmx">
<soapenv:Header/>
<soapenv:Body>
<req:GetProduct>
<req:TransactionID>id</req:TransactionID>
<!--Optional:-->
<req:UserName>username</req:UserName>
</req:GetProduct>
Parameters name have an extra :req prefix that may cause my problem. When I add the req: to my java parameters name, an exception occurs:
java.io.IOException: Server returned HTTP response code: 400 for URL: http://whatever.com/WebService/Request.asmx?wsdl/
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
at sun.net.www.protocol.http.HttpURLConnection$6.run(HttpURLConnection.java:1345)
at java.security.AccessController.doPrivileged(Native Method)
at sun.net.www.protocol.http.HttpURLConnection.getChainedException(HttpURLConnection.java:1339)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:993)
at org.ksoap2.transport.ServiceConnectionSE.openInputStream(ServiceConnectionSE.java:113)
at org.ksoap2.transport.HttpTransportSE.call(HttpTransportSE.java:184)
at org.ksoap2.transport.HttpTransportSE.call(HttpTransportSE.java:96)
at SoapCommand.invokeMethod(SoapCommand.java:42)
at SoapCommand.main(SoapCommand.java:16)
Caused by: java.io.IOException: Server returned HTTP response code: 400 for URL: http://whatever.com/WebService/Request.asmx?wsdl/
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1290)
at sun.net.www.protocol.http.HttpURLConnection.getHeaderFields(HttpURLConnection.java:2129)
at org.ksoap2.transport.ServiceConnectionSE.getResponseProperties(ServiceConnectionSE.java:84)
at org.ksoap2.transport.HttpTransportSE.call(HttpTransportSE.java:167)
... 3 more
Exception in thread "main" java.lang.NullPointerException
at SoapCommand.main(SoapCommand.java:17)
It's driving me crazy.
There is nothing wrong with your code. The req prefix is as per SOAP/XML standards. The problem lies in the way the webservice is deployed and the way ksoap works.
All webservices are expected to respond back with the WSDL if you append ?wsdl to the webservice endpoint. ksoap expects this and requests the WSDL. In this case, the webservice doesn't send back the WSDL, throwing a 400 error.
You should ask the webservice provider why the WSDL doesn't show up when you send a request for http://whatever.com/WebService/Request.asmx?wsdl.
If that doesn't work out, consider creating a SOAP client on a server, connecting to the webservice, and call your server with a REST webservice from Android. That way, you use less bandwidth and do less processing on the device.
I'm using the Apache Amber libraries to try to retrieve an OAuth2 access token from a Web site under my control. My client code is running under Android.
My code is patterned on the example at:
https://cwiki.apache.org/confluence/display/AMBER/OAuth+2.0+Client+Quickstart
In the first step, I'm able to retrieve a "code" by submitting a GET request using a WebView browser:
OAuthClientRequest request = OAuthClientRequest
.authorizationLocation(AUTHORIZE_URL)
.setClientId(CLIENT_ID)
.setRedirectURI(REDIR_URL)
.setResponseType(CODE_RESPONSE)
.buildQueryMessage();
webview.loadUrl(request.getLocationUri());
I use a WebViewClient callback to capture the redirect URL with the "code" parameter. So far, so good.
Using that code, I try to retrieve my access token:
OAuthClient oAuthClient = new OAuthClient(new URLConnectionClient());
OAuthClientRequest request = OAuthClientRequest
.tokenLocation(ACCESS_TOKEN_URL)
.setGrantType(GrantType.AUTHORIZATION_CODE)
.setClientId(CLIENT_ID)
.setClientSecret(CLIENT_SECRET)
.setRedirectURI(REDIR_URL)
.setCode(code)
.buildBodyMessage();
GitHubTokenResponse oAuthResponse =
oAuthClient.accessToken(request, GitHubTokenResponse.class);
Each time I run my code, I get an OAuthProblemException, where the message is that I have an invalid request due to a missing parameter, access_token.
Another StackOverflow post mentions this exception from a similar OAuth2 request, which in that case was caused by having different redirect URIs across OAuth requests. But I've made sure my redirect URIs are the same by using a named constant. Here's the link to that post:
OAuthProblem, missing parameter access_token
Now, I can print out the code returned by the first request, and paste it into a curl command run from my desktop machine:
curl -d "code=...&client_id=...&client_secret=...&grant_type=...&redirect_uri=..." http://my_website.com
and I get a nice JSON response from my site with an access_token.
Why does the call from Java fail, where my hand-rolled command line succeeds?
I had the same problem implementing the client and the server, the problem is about one mistake in the Client Example in the Apache Amber (Oltu) project:
First you have the Auth code request (which work):
OAuthClientRequest request = OAuthClientRequest
.authorizationLocation(AUTHORIZE_URL)
.setClientId(CLIENT_ID)
.setRedirectURI(REDIR_URL)
.setResponseType(CODE_RESPONSE)
.**buildQueryMessage**();
And second the request about the Access Token (which don't work):
OAuthClientRequest request = OAuthClientRequest
.tokenLocation(ACCESS_TOKEN_URL)
.setGrantType(GrantType.AUTHORIZATION_CODE)
.setClientId(CLIENT_ID)
.setClientSecret(CLIENT_SECRET)
.setRedirectURI(REDIR_URL)
.setCode(code)
.**buildBodyMessage**();
The mistake is about the buildBodyMessage() in the second request. Change it by buildQueryMessage().
Solved in my case.
Amber/Oltu "Missing parameter access_token" error may mean that GitHubTokenResponse or OAuthJSONAccessTokenResponse are unabled to translate response body for any reason. In my case (with Google+ oAuth2 authentication), the response body, is not parsed properly to the inner parameters map.
For example:
GitHubTokenResponse
parameters = OAuthUtils.decodeForm(body);
Parse a form-urlencoded result body
... and OAuthJSONAccessTokenResponse has the next parse function
parameters = JSONUtils.parseJSON(body);
This JSONUtils.parseJSON is a custom JSON parser that not allow for me JSON response body from GOOGLE+ and throws an JSONError (console not logged),
Each error throwed parsing this parameters, are not console visible, and then always is throwed doomed "Missing parameter: access_token" or another "missing parameter" error.
If you write your Custom OAuthAccessTokenResponse, you can see response body, and write a parser that works with your response.
This is what I encountered and what I did to get it working:
I quickly put together a similar example described in:
https://cwiki.apache.org/confluence/display/OLTU/OAuth+2.0+Client+Quickstart
and:
svn.apache.org/repos/asf/oltu/trunk/oauth-2.0/client/src/test/java/org/apache/oltu/oauth2/client/OAuthClientTest.java
This was my command to execute it:
java -cp .:./org.apache.oltu.oauth2.client-1.0.1-20150221.171248-36.jar OAuthClientTest
I also ended up with the above mentioned error where the access_token was expected. I ended up debugging in intellij and traced an anomaly with the if condition which checks that the string begins with the "{" character.
In doing so, I also added the following jar to my classpath so that I may debug the trace a little deeper.
./java-json.jar
(downloaded from http://www.java2s.com/Code/Jar/j/Downloadjavajsonjar.htm)
During the next debug session, the code actually started working. My mate and I eventually found the root cause was due to the JSON jar not being included.
This is the command which works:
java -cp .:./java-json.jar:./org.apache.oltu.oauth2.client-1.0.1-20150221.171248-36.jar OAuthClientTest
I was having the same problem when trying to get the access token from fitbit OAuth2. buildBodyMessage() and buildQueryMessage() were both giving me missing parameter, access_token.
I believe this is something to do with the apache oauth2 client library. I ended up making simple post requests using spring's RestTemplate and it's working fine.
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
headers.set("Authorization", "Basic " + "MjI5TkRZOjAwNDBhNDBkMjRmZTA0OTJhNTE5NzU5NmQ1N2ZmZGEw");
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.add("client_id", FITBIT_CLIENT_ID);
map.add("grant_type", "authorization_code");
map.add("redirect_uri", Constants.RESTFUL_PATH + "/fitbit/fitbitredirect");
map.add("code", code);
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(map, headers);
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response = restTemplate.postForEntity(FITBIT_TOKEN_URI, request, String.class);
log.debug("response.body: " + response.getBody());
I'm developing an Android app which needs to connect to a .NET SOAP web service and generate/populate a number of objects from the response. I'm totally new to web services and SOAP, and relatively new to Android. The web service has already been built (not by me).
I've been trying to find info on connecting to SOAP web services in Android. The two basic suggestions seem to be:
Don't use SOAP,
If you do use SOAP, use KSOAP2.
I've looked at various tutorials for KSOAP2 but they all seem to deal only with the most basic, primitive types, such as sending 2 ints to get 1 int back, or sending and receiving strings;, however, I need to send custom objects back and forth.
Is it possible to send and receive custom objects using KSOAP2? If so, how easy/difficult is this? Does the XML have to be parsed to create/populate the objects "by hand"?
EDIT/UPDATE:
After trying for a while to connect to the existing webservice (takes a String and returns a complex object), and getting an error, I decided to try a simpler approach. I connected to a simpler webservice which takes no parameters, and returns an int. This simpler webservice is hosted in the same place as the original one, and the simple one now works fine for me.
FURTHER UPDATE:
All working fine now! Turns out I just had a capitalisation error which was throwing off my parameter.
The final problem was "national" versus "National". Here's the soapUI-generated code:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/">
<soapenv:Header/>
<soapenv:Body>
<tem:GetClientByNationalID>
<!--Optional:-->
<tem:nationalID>XXXXXXX</tem:nationalID>
</tem:GetClientByNationalID>
</soapenv:Body>
</soapenv:Envelope>
And the request code which was being generated by my Java:
<v:Envelope xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://www.w3.org/2001/XMLSchema" xmlns:c="http://schemas.xmlsoap.org/soap/encoding/" xmlns:v="http://schemas.xmlsoap.org/soap/envelope/">
<v:Header />
<v:Body>
<GetClientByNationalID xmlns="http://tempuri.org/" id="o0" c:root="1">
<NationalID i:type="d:string">
XXXXXXXX
</NationalID>
</GetClientByNationalID>
</v:Body>
</v:Envelope>
My final java code is:
public void webServiceCall() {
String NAMESPACE = "http://tempuri.org/";
String METHOD_NAME = "GetClientByNationalID";
String SOAP_ACTION = "http://tempuri.org/IClientService/GetClientByNationalID";
// unsecure service
String URL = "http://XXXXXXXXXXXXXXXXXXXX.net/FPUnSecureService/ClientService.svc";
SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME);
String clientID = "XXXXXXX";
PropertyInfo pi = new PropertyInfo();
pi.setName("nationalID");
pi.setValue(clientID);
pi.setType(clientID.getClass());
request.addProperty(pi);
SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
envelope.dotNet = true;
envelope.setOutputSoapObject(request);
envelope.addMapping(NAMESPACE, "GetClientByNationalID", new ClientID().getClass());
Marshal floatMarshal = new MarshalFloat();
floatMarshal.register(envelope);
HttpTransportSE t = new HttpTransportSE(URL);
ClientID client = new ClientID();
t.debug = true;
try {
Log.i("webServiceCall", "Trying call to web service");
t.call(SOAP_ACTION, envelope);
SoapObject response = (SoapObject) envelope.getResponse();
Log.i("success", response.getPropertyAsString(0));
Log.i("success", response.getPropertyAsString(1));
Log.i("success", response.getPropertyAsString(2));
Log.i("success", response.getPropertyAsString(3));
Log.i("success", response.getPropertyAsString(4));
} catch (Exception e) {
Log.e("webServiceCall", "Error calling webservice.");
e.printStackTrace();
System.out.println("Request: " + t.requestDump);
System.out.println("Response: " + t.responseDump);
}
}
I am still confused about these lines:
envelope.addMapping(NAMESPACE, "GetClientByNationalID", new ClientID().
Marshal floatMarshal = new MarshalFloat();
floatMarshal.register(envelope);
I think I added the Marshal parts when I was passing an object as a parameter, I'm not sure if I still need them. I'm also not sure about the addMapping call either, and what I should have in there. Thanks.
Use ksoap2, and check my answers with code examples at the following links: Link1,link2,link3. I wrote some details which will help you understand how to start coding.
let me know if u need help.
UPDATE ( Answering your question about mapping and marshalling)
u use addMapping when you are Sending a complex type ( ie an object) through the soap envelope. How will the webservice recognize the complex type ? u need to create locally a class that implement kvmserializable (as mentionned in my links) which will have same parameter as the object on the server, and then u need to add mapping between it and the class that maps to it on the server, so that the enveloppe when parsed on the server it knows this complex type X map to class X on the server. So if you are not Sending complex type, you don't need to add mapping.(PS:I see that your not sending a complex type since nationalID is of type string. IF lets say nationalID was a complex type of type Z you would do : addMapping(NAMESPACE, Z.class.getSimpleName(), Z.class) )
As for marshalling, it uses java serialization to change Objects to stream of data to be unmarshalled on the web service. So when u are sending a complex type,based on my experience, it is a good practice to add marshalling to change the complex type to streams of data by serializing it. If you find that you don't need it just don't add it, but its always good to understand what are the options out there.
Look at this http://seesharpgears.blogspot.com/2010/10/ksoap-android-web-service-tutorial-with.html , there are examples of how to pass complex objects.
I want to access a Restful web service. I want the request should be in the following format.
GET /API/Contacts/username HTTP/1.1
HOST: $baseuri:port
Accept: text/xml
Authorization: Basic ZmF0aWdhYmxlIGdlbmVyYXR=
And also I am calling the web service though HTTPS protocol.
The folowing is the code I am using :
HttpGet get = new HttpGet("https://secure.myapp.com/MyApp/API/Contacts/myname");
get.addHeader("Accept","text/xml");
get.addHeader("Authorization","Basic ZmF0aWdhYmxlIGdlbmVyYXR=");
get.addHeader("Host","https://secure.myapp.com");
get.addHeader("Connection Use","HTTP 1.1");
DefaultHttpClient client = new DefaultHttpClient();
ResponseHandler objHandler = new BasicResponseHandler();
String getResponse = client.execute(get,objHandler);
But I am getting an Error : 400 Bad request.
I am not sure whether my code is correct. Is it necessary to specify the method (GET, POST or PUT) explicitly in the header?
Please help me...
Thnking You....
Shouldn't you put 'Basic ZmF0aWdhYmxlIGdlbmVyYXR=' in double quotes?
Also check web service example request and response and make sure that everything is specified.
Ohh...
That was my mistake.
I did not keep the order of adding headers.
When I changed its order according to the Request it is working fine.
In the request the HOST should be the first parameter.
The following is the corrected code.
get.addHeader("Host","secure.myapp.com");
get.addHeader("Accept","text/xml");
get.addHeader("Authorization","Basic ZmF0aWdhYmxlIGdlbmVyYXR=");
get.addHeader("Connection Use","HTTP 1.1");
Sorry to disturb you all...