We have an old application written in Java that uses and old version of wss4j to generate the SOAP requests and xFire to transmit those requests.
This project uses a few dependencies that I believe are relevant:
wss4j (1.6.5)
xfire-all (1.2.6)
xfire-jsr181-api (1.0-M1)
Frankly, I have little experience with SOAP and zero experience with this wss4j library.
From looking online, I was unable to find a lot of helpful resources for this older version of wss4j.
With all of the developers who worked on this project having left the company before I joined, I'm hoping to find some help from the vast knowledge found on SO.
A change in the SOAP service has been made by an external team for which we need to modify our client.
We need to be able to add a BinarySecurityToken to the Security section of the header as well as an arbitrary element to the Header.
From what I've read online, the BinarySecurityToken is supposed to be used to encode certificate data, so I'm not sure if we're trying to use it correctly, but frankly we don't have a choice as the change has been made to the service and we have to adapt the client to meet it's new requirements :(
Below is an example or what our application currently sends; it does not include a BinarySecurityToken in the Security element or an authMethod element in the Header element:
<soap:Header>
<wsse:Security soap:mustUnderstand="1">
<wsse:UsernameToken xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="SecurityToken-b92109ac-4a13-444d-b8e5-19c400f5910f">
<wsse:Username>some_username</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">some_password</wsse:Password>
<wsse:Nonce>Daaaaa1bBbbbbBb2cccccc==</wsse:Nonce>
<wsu:Created>2022-09-19T09:10:31Z</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>
<wsa:Action>some_text</wsa:Action>
<wsa:MessageID>some_text_value</wsa:MessageID>
<wsa:ReplyTo>
<wsa:Address>some_text_value</wsa:Address>
</wsa:ReplyTo>
<wsa:To endpointID="some_text_value">http://172.31.9.216:443/P/V3_20090316L/XML/FICR_AR062004CA</wsa:To>
<wsa:From endpointID="some_text_value">
<wsa:Address>some_text_value</wsa:Address>
</wsa:From>
<language>en</language>
</soap:Header>
The expected SOAP request must contain a header that looks like the below, it must include a BinarySecurityToken in the Security element and an authMethod element in the Header element:
<soap:Header>
<wsse:Security soap:mustUnderstand="1">
<wsse:UsernameToken xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="SecurityToken-b92109ac-4a13-444d-b8e5-19c400f5910f">
<wsse:Username>some_username</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText"> </wsse:Password>
<wsse:Nonce>Daaaaa1bBbbbbBb2cccccc==</wsse:Nonce>
<wsu:Created>2022-09-19T09:10:31Z</wsu:Created>
</wsse:UsernameToken>
<wsse:BinarySecurityToken ValueType="authToken" EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="SecurityToken-c0c6c469-235a-42e5-869c-cec77b1c2dbd">some_token_which_will_be_base64_encoded</wsse:BinarySecurityToken>
</wsse:Security>
<wsa:Action>some_text</wsa:Action>
<wsa:MessageID>some_text_value</wsa:MessageID>
<wsa:ReplyTo>
<wsa:Address>some_text_value</wsa:Address>
</wsa:ReplyTo>
<wsa:To endpointID="some_text_value">http://172.31.9.216:443/P/V3_20090316L/XML/FICR_AR062004CA</wsa:To>
<wsa:From endpointID="some_text_value">
<wsa:Address>some_text_value</wsa:Address>
</wsa:From>
<language>en</language>
<ehr:authMethod xmlns:ehr="some_text_value">authToken</ehr:authMethod>
</soap:Header>
Below an a sample of the current code:
I cleaned-up the code a little to make it legible and added full class-declarations where I thought important.
Transmitter.java
public class Transmitter
{
public org.w3c.dom.Document sendRequest(
Document reqMsg,
String serverUrlAndLocation,
String action,
String msgId,
String receiverNetAddr,
String senderNetAddr,
String toEndpointID,
String fromEndpointID,
String username,
String authToken,
String testCase
)
final EHIP_routingServiceClient prclient = new EHIP_routingServiceClient(serverUrlAndLocation);
final EHIP_routingPortType prpt = prclient.getEHIP_routingPort0();
final org.codehaus.xfire.client.Client client = org.codehaus.xfire.client.Client.getInstance(prpt);
String interactionId = stripVersion(action);
action = "urn:hl7-org:v3:" + action;
final RxDOMInHandler rxHandler = new RxDOMInHandler(testCase);
client.addInHandler (rxHandler); // creates the DOM for the SOAP msg;
client.addOutHandler (new org.codehaus.xfire.util.dom.DOMOutHandler.DOMOutHandler());
client.addFaultHandler(new org.codehaus.xfire.util.dom.DOMOutHandler.DOMOutHandler());
client.addInHandler (new org.codehaus.xfire.util.LoggingHandler());
client.addOutHandler (new RxLoggingHandler(testCase, interactionId));
client.addFaultHandler(new RxLoggingHandler(testCase, interactionId));
this.enableUsernameTokenEClaims(
client,
username,
authToken
);
this.enableWSAddressing(
client,
action,
msgId,
receiverNetAddr,
senderNetAddr,
toEndpointID,
fromEndpointID
);
client.addOutHandler(new RxSoapActionOutHandler(action, false));
client.addOutHandler(new RxAdjustBodyOutHandler(m_api.getCreationTimeFormatFlag()));
final Object[] params = new Object[] {reqMsg};
Object[] ret;
try
{
ret = client.invoke("processRequest", params);
}
catch(XFireFault fault)
{
if (fault.getCause() instanceof WstxEOFException)
{
final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
final DocumentBuilder documentBuilder = factory.newDocumentBuilder();
ret = new Document[1];
ret[0] = documentBuilder.newDocument();
}
else
{
throw fault;
}
}
return rxHandler.getResponse();
}
private void enableUsernameTokenEClaims(
Client client,
String username,
String authToken
)
{
final java.util.Map<String, Object> usernameTokenConfig = new HashMap<String, Object>();
usernameTokenConfig.put(WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN);
usernameTokenConfig.put(WSHandlerConstants.USER, username);
usernameTokenConfig.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_DIGEST);
final RxPasswordHandler pwdHandler = new RxPasswordHandler();
pwdHandler.setPassword(authToken, true);
usernameTokenConfig.put(WSHandlerConstants.PW_CALLBACK_REF, pwdHandler);
client.addOutHandler(new WSS4JOutHandler(usernameTokenConfig));
}
private void enableWSAddressing(
org.codehaus.xfire.client.Client client,
String action,
String msgId,
String receiverNetAddr,
String senderNetAddr,
String toEndpointID,
String fromEndpointID
)
{
final RxAddressingHeadersFactory200508 factory = new RxAddressingHeadersFactory200508(
m_api.getLanguage(),
toEndpointID,
fromEndpointID
);
final AddressingHeaders ah = new AddressingHeaders();
if (msgId == null || msgId.trim().equals(""))
{
ah.setMessageID(new RandomGUID(true).toString());
}
else
{
ah.setMessageID(msgId);
}
final EndpointReference epr1 = factory.createEClaimsDefaultEPR(senderNetAddr);
ah.setFrom(epr1);
ah.setReplyTo(factory.createDefaultEPR());
ah.setTo(receiverNetAddr);
ah.setAction(action);
final java.util.Map<String, Object> properties = new HashMap<String, Object> ();
properties.put(AddressingInHandler.ADRESSING_HEADERS.toString(), ah);
properties.put(AddressingInHandler.ADRESSING_FACTORY.toString(), factory);
client.addOutHandler(new RxAddressingOutHandler(properties));
}
}
RxDOMInHandler
public class RxDOMInHandler extends org.codehaus.xfire.util.dom.DOMInHandler
{
public static final String DOM_MESSAGE = "dom.message";
private String interactionId = null;
private String testCase = null;
private org.w3c.dom.Document response = null;
public RxDOMInHandler()
{
super();
setPhase(org.codehaus.xfire.handler.Phase.PARSE);
before(org.codehaus.xfire.soap.handler.ReadHeadersHandler.class.getName());
}
public RxDOMInHandler(String testCase)
{
super();
setPhase(org.codehaus.xfire.handler.Phase.PARSE);
before(org.codehaus.xfire.soap.handler.ReadHeadersHandler.class.getName());
this.testCase = testCase;
}
public void invoke(org.codehaus.xfire.MessageContext context)
throws Exception
{
org.w3c.dom.Document doc = null;
javax.xml.parsers.DocumentBuilderFactory dbf = javax.xml.parsers.DocumentBuilderFactory.newInstance();
dbf.setValidating(false);
dbf.setIgnoringComments(false);
dbf.setIgnoringElementContentWhitespace(false);
dbf.setNamespaceAware(true);
dbf.setCoalescing(false);
org.codehaus.stax2.XMLStreamReader2 reader2 = (org.codehaus.stax2.XMLStreamReader2) context.getInMessage().getXMLStreamReader();
doc = org.codehaus.xfire.util.STAXUtils.read(dbf.newDocumentBuilder(), reader2, false);
this.response = doc;
context.getInMessage().setProperty(DOM_MESSAGE, doc);
context.getInMessage().setXMLStreamReader(new org.codehaus.xfire.util.stax.W3CDOMStreamReader(doc.getDocumentElement()));
this.interactionId = RxDOMUtil.getInteractionIdFromDoc(doc);
RxDOMUtil.writeXmlToFile("xmls/SOAP_Response_" + this.interactionId + "_" + this.testCase + ".xml", doc);
}
public org.w3c.dom.Document getResponse()
{
return response;
}
}
I tried modifying the enableUsernameTokenEClaims method to add a BinarySecurityToken, but so far I have not been able to get that to work.
I have already successfully make this same change to another client of this service, but that client was written in VB and the libraries being used there were either more flexible or less strict.
Based on speaking with coworkers with more experience with these libraries and SOAP in general, it seems that this version of the wss4j library just doesn't support BinarySecurityToken :(
As such we're going to go a different route to transmit the tokens.
Related
I'm attempting to use Retrofit to call the GitHub API to update the contents of an existing file, but am getting 404s in my responses. For this question, I'm interested in updating this file. Here is the main code I wrote to try and achieve this:
GitHubUpdateFileRequest
public class GitHubUpdateFileRequest {
public String message = "Some commit message";
public String content = "Hello World!!";
public String sha = "shaRetrievedFromSuccessfulGETOperation";
public final Committer committer = new Committer();
private class Committer {
Author author = new Author();
private class Author {
final String name = "blakewilliams1";
final String email = "blake#blakewilliams.org";
}
}
}
**GitHubUpdateFileResponse **
public class GitHubUpdateFileResponse {
public GitHubUpdateFileResponse() {}
}
GitHubClient
public interface GitHubClient {
// Docs: https://docs.github.com/en/rest/reference/repos#get-repository-content
// WORKS FINE
#GET("/repos/blakewilliams1/blakewilliams1.github.io/contents/qr_config.json")
Call<GitHubFile> getConfigFile();
// https://docs.github.com/en/rest/reference/repos#create-or-update-file-contents
// DOES NOT WORK
#PUT("/repos/blakewilliams1/blakewilliams1.github.io/contents/qr_config.json")
Call<GitHubUpdateFileResponse> updateConfigFile(#Body GitHubUpdateFileRequest request);
}
Main Logic
// Set up the Retrofit client and add an authorization interceptor
UserAuthInterceptor interceptor =
new UserAuthInterceptor("blake#blakewilliams.org", "myActualGitHubPassword");
OkHttpClient.Builder httpClient =
new OkHttpClient.Builder().addInterceptor(interceptor);
Retrofit.Builder builder =
new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create());
Retrofit retrofit = builder.client(httpClient.build()).build();
client = retrofit.create(GitHubClient.class);
// Now make the request and process the response
GitHubUpdateFileRequest request = new GitHubUpdateFileRequest();
client.updateConfigFile(request).enqueue(new Callback<GitHubUpdateFileResponse>() {
#Override
public void onResponse(Call<GitHubUpdateFileResponse> call, Response<GitHubUpdateFileResponse> response) {
int responseCode = response.code();
// More code on successful update
}
#Override
public void onFailure(Call<GitHubUpdateFileResponse> call, Throwable t) {
Log.e("MainActivity", "Unable to update file" + t.getLocalizedMessage());
}
});
What currently happens:
Currently, the success callback is triggered, but with a response code of 404 like so:
Response{protocol=http/1.1, code=404, message=Not Found, url=https://api.github.com/repos/blakewilliams1/blakewilliams1.github.io/contents/qr_config.json}
Has anyone else encountered this? I first thought it was a problem with including '/content/' in the URL but I do the same thing for reading the file contents request and it works fine (also uses same URL just a GET instead of PUT).
For anyone interested in doing this in the future, I figured out the solution.
I needed to revise the request object structure
Rather than using an authentication interceptor, I instead added an access token to the header. Here is where you can create access tokens for Github, you only need to grant it permissions to the 'repos' options for this use case to work.
This is what my updated request object looks like:
public class GitHubUpdateFileRequest {
public String message;
public String content;
public String sha;
public final Committer committer = new Committer();
public GitHubUpdateFileRequest(String unencodedContent, String message, String sha) {
this.message = message;
this.content = Base64.getEncoder().encodeToString(unencodedContent.getBytes());
this.sha = sha;
}
private static class Committer {
final String name = "yourGithubUsername";
final String email = "email#yourEmailAddressForTheUsername.com";
}
}
Then from my code, I would just say:
GitHubUpdateFileRequest updateRequest = new GitHubUpdateFileRequest("Hello World File Contents", "This is the title of the commit", shaOfExistingFile);
For using this reqest, I updated the Retrofit client implementation like so:
// https://docs.github.com/en/rest/reference/repos#create-or-update-file-contents
#Headers({"Content-Type: application/vnd.github.v3+json"})
#PUT("/repos/yourUserName/yourRepository/subfolder/path/to/specific/file/theFile.txt")
Call<GitHubUpdateFileResponse> updateConfigFile(
#Header("Authorization") String authorization, #Body GitHubUpdateFileRequest request);
And I call that interface like this:
githubClient.updateConfigFile("token yourGeneratedGithubToken", request);
And yes, you do need the prefix "token ". You could hardcode that header into the interface, but I pass it in so that I can store it in locations outside of my version control's reach for security reasons.
I want to retrieve Common Name (CN) property from client certificate in SOAP communication. I'm using Spring WebServiceTemplate to create my webservice endpoint. I have already implemented WS mutual authentication following the example.
Is there any solution to obtain certificate details from client request by means of WebServiceTemplate or some other library?
Fortunately, I have managed to figure it out!
Spring WS provides very convenient way to retrieve the X509Certificate.
Normally, You have an endpoint like this:
#Endpoint
public class CountryEndpoint {
private static final String NAMESPACE_URI = "http://spring.io/guides/gs-producing-web-service";
...
#PayloadRoot(namespace = NAMESPACE_URI, localPart = "getCountryRequest")
#ResponsePayload
public GetCountryResponse getCountry(#RequestPayload GetCountryRequest request) {
//method body here
return response;
}
}
However, Spring allows to add additional parameters the method annotated as #PayloadRoot. It can be a MessageContext instance.
public GetCountryResponse getCountry(#RequestPayload MessageContext context, #RequestPayload GetCountryRequest request)`
Then You will be able to obtain the wsse:Security header as follows:
WebServiceMessage webServiceMessageRequest = context.getRequest();
SaajSoapMessage saajSoapMessage = (SaajSoapMessage) webServiceMessageRequest;
SOAPMessage doc = saajSoapMessage.getSaajMessage();
Element elem = WSSecurityUtil.getSecurityHeader(doc.getSOAPPart(), "");
Now get the right content of BinarySecurityToken tag:
String binarySecurityToken = elem.getElementsByTagName("BinarySecurityToken").item(0).getTextContent();
At the end, you should recreate the X509Certificate by passing binarySecurityToken as its constructor parameter. Later You can extract CN by many different ways for example by means of LDAP utlis.
There is another way.
Create AbstractSoapInterceptor with this body :
private final static QName SECURITY_QNAME = new QName("http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", "Security", "");
private static CertificateFactory certFactory;
public xxx() throws CertificateException {
super(Phase.PRE_PROTOCOL);
certFactory = CertificateFactory.getInstance("X.509");
}
#SneakyThrows
#Override
public void handleMessage(SoapMessage message) throws Fault {
SoapHeader header = (SoapHeader) message.getHeader(SECURITY_QNAME);
Node binarySignatureTag = ((Element) header.getObject()).getFirstChild();
BinarySecurity token = new X509Security((Element) binarySignatureTag, new BSPEnforcer());
InputStream in = new ByteArrayInputStream(token.getToken());
X509Certificate cert = (X509Certificate) certFactory.generateCertificate(in);
}
Register it in configuration of endpoint :
#Bean
public Endpoint endpoint() throws CertificateException {
EndpointImpl endpoint = new EndpointImpl(springBus(), xxxPortType());
endpoint.setServiceName(xxxService().getServiceName());
endpoint.publish("/xxxx");
endpoint.getInInterceptors().add(new xxx());
return endpoint;
}
I am trying to send a request to a SOAP webservice. I read this tutorial and prepared the following code. However, I am going to send different requests to multiple SOAP webservices, whereas the tutorial focused on one request. How can I send SOAP request using WebserviceTemplate?
WebServiceTemplate
SoapMessage soapMsg = new SoapMessage();
soapMsg.setUsername("Requester");
soapMsg.setPassword("Pass");
soapMsg.setLanguageCode("EN");
Request request = new Request();
request.setDeparture("FDH");
request.setDestination("HAM");
Date date = new Date();
SimpleDateFormat frm2 = new SimpleDateFormat("yyyy-MM-dd");
request.setDepartureDate(frm2.parse(frm2.format(date)));
request.setNumADT(1);
request.setNumCHD(0);
request.setNumInf(0);
request.setCurrencyCode("EUR");
request.setWaitForResult(true);
request.setNearByDepartures(true);
request.setNearByDestinations(true);
request.setRronly(false);
request.setMetaSearch(false);
soapMsg.setRequest(request);
WebServiceTemplate webServiceTemplate = new WebServiceTemplate(). //how to create object and send request!
Object response = webServiceTemplate.marshalSendAndReceive(
"https://aaa5.elsyarres.net", soapMsg);
Response msg = (Response) response;
System.err.println("size of results of wogolo:"
+ msg.getFlights().getFlight().size());
You can use following code, you do not need to define anything in xml file.
try {
SaajSoapMessageFactory messageFactory = new SaajSoapMessageFactory(
MessageFactory.newInstance());
messageFactory.afterPropertiesSet();
WebServiceTemplate webServiceTemplate = new WebServiceTemplate(
messageFactory);
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setContextPath("PACKAGE");
marshaller.afterPropertiesSet();
webServiceTemplate.setMarshaller(marshaller);
webServiceTemplate.afterPropertiesSet();
Response response = (Response) webServiceTemplate
.marshalSendAndReceive(
"address",
searchFlights);
Response msg = (Response) response;
} catch (Exception s) {
s.printStackTrace();
}
To send different SOAP requests to different SOAP services, you just need to make your WebServiceTemplate aware of all requests and responses it will have to process.
Create a Java class for each request and response like so:
package models;
import javax.xml.bind.annotation.XmlRootElement;
import java.io.Serializable;
#XmlRootElement
public class FlyRequest implements Serializable {
private boolean nearByDeparture;
public FlyRequest() {}
public boolean isNearByDeparture() {
return nearByDeparture;
}
public void setNearByDeparture(boolean nearByDeparture) {
this.nearByDeparture = nearByDeparture;
}
}
(The #XmlRootElement is because we use JAXB marshaller below; see Jaxb reference for more info).
The setup of the template is done for example like so:
SaajSoapMessageFactory messageFactory = new SaajSoapMessageFactory(MessageFactory.newInstance());
messageFactory.afterPropertiesSet();
WebServiceTemplate webServiceTemplate = new WebServiceTemplate(messageFactory);
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setContextPath("models");
marshaller.afterPropertiesSet();
webServiceTemplate.setMarshaller(marshaller);
webServiceTemplate.afterPropertiesSet();
"models" is the name of the package where the Request/Responses classes are, so that jaxb can find them.
Then you just instantiate the request of the class you want to perform the call, like so:
// call fly service:
FlyRequest flyRequest = new FlyRequest();
flyRequest.setNearByDeparture(false);
Object flyResponse = webServiceTemplate.marshalSendAndReceive("https://example.net/fly", flyRequest);
// call purchase service:
PurchaseRequest purchaseRequest = new PurchaseRequest();
purchaseRequest.setPrice(100);
Object purchaseResponse = webServiceTemplate.marshalSendAndReceive("https://example.net/purchase", purchaseRequest);
Similarly, you can cast the response objects into your JAXB classes defined above.
Here is an Example what you should be looking for
Soap has a lot of restriction unlike REST, It follows some standards which have to be meet before you get Network call to work,
But unlike Rest, in Soap if you have WSDL URL you can get all the information needed to call the Soap call
private final String NAMESPACE = "http://www.w3schools.com/webservices/";
private final String URL = "http://www.w3schools.com/webservices/tempconvert.asmx?WSDL";
private final String SOAP_ACTION = "http://www.w3schools.com/webservices/CelsiusToFahrenheit";
private final String METHOD_NAME = "CelsiusToFahrenheit";
this code was written in Android so you can ignore some part of it but I still kept it in answer so someone from android background can put a good use to it
Open [WSDL][1] in browser and check for the things which matter to call a remote method on server.
1
you will see an attribute targetNamespace whose value would be Namespace which you will use in this case Namespace is http://www.w3schools.com/webservices/
2
Now you require the name of the method this WSDL has four method each of the are int attribute s:element with the value is the name of the Method in this case four methods are FahrenheitToCelsius, FahrenheitToCelsiusResponse, CelsiusToFahrenheit, CelsiusToFahrenheitResponse
3
Now you have to fure out the SOAP Action which is NAMESPACE+METHOD
but WSDL also gives information about that as well, look for the tag soap:operation and it's soapAction attribute havs the Soap action as it's value in this case which we want to call is http://www.w3schools.com/webservices/CelsiusToFahrenheit
private class MyTask extends AsyncTask<Void, Void, String> {
#Override
protected void onPreExecute() {
super.onPreExecute();
progressDialog.show();
}
#Override
protected String doInBackground(Void... params) {
try {
SoapObject soapObject = new SoapObject(NAMESPACE, METHOD_NAME);
soapObject.addProperty("Celsius","12");
SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
envelope.dotNet = true;
envelope.setOutputSoapObject(soapObject);
HttpTransportSE httpTransportSE = new HttpTransportSE(URL);
httpTransportSE.call(SOAP_ACTION, envelope);
SoapPrimitive soapPrimitive = (SoapPrimitive)envelope.getResponse();
Log.d("TAG", "doInBackground: "+soapPrimitive.toString());
return soapObject.toString();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(String aVoid) {
super.onPostExecute(aVoid);
progressDialog.dismiss();
textView.setText(""+aVoid);
}
}
Assuming that your SoapMessage is marhsallable
To send the same message to multiple endpoints you only need to loop on the sending code and the request handler.
Something like this:
{
String endpoint = "https://aaa5.elsyarres.net"
WebServiceTemplate webServiceTemplate = new WebServiceTemplate().
webServiceTemplate.setDefaultUri(endpoint);
Object response = webServiceTemplate.marshalSendAndReceive(soapMsg);
// handle you are response as you are currently doing.
// Loop changing the endpoint as you need.
}
This code uses the Spring WebServiceTemplate
I tried many options and finally below one worked for me if you have to send soap header with authentication(Provided authentication object created by wsimport) and also need to set soapaction.
public Response callWebService(String url, Object request)
{
Response res = null;
log.info("The request object is " + request.toString());
try {
res = (Response) getWebServiceTemplate().marshalSendAndReceive(url, request,new WebServiceMessageCallback() {
#Override
public void doWithMessage(WebServiceMessage message) {
try {
// get the header from the SOAP message
SoapHeader soapHeader = ((SoapMessage) message).getSoapHeader();
// create the header element
ObjectFactory factory = new ObjectFactory();
Authentication auth =
factory.createAuthentication();
auth.setUser("****");
auth.setPassword("******");
((SoapMessage) message).setSoapAction(
"soapAction");
JAXBElement<Authentication> headers =
factory.createAuthentication(auth);
// create a marshaller
JAXBContext context = JAXBContext.newInstance(Authentication.class);
Marshaller marshaller = context.createMarshaller();
// marshal the headers into the specified result
marshaller.marshal(headers, soapHeader.getResult());
} catch (Exception e) {
log.error("error during marshalling of the SOAP headers", e);
}
}
});
} catch (Exception e) {
e.printStackTrace();
}
return res;
}
I'm trying to delete a file from a Sharepoint list in Java and running into some issues. I'm using a batch element described here
I'm able to make the request, but the results that come back are null and the file is not deleted (I don't get any errors).
Here is the code for the UpdateListItems.Update that I'm using:
UpdateListItems.Updates updates = new UpdateListItems.Updates();
updates.getContent().add(this.generateXmlNode(
"<Batch PreCalc='True' OnError='Continue' ListVersion='1' ListName='" + spMoveRequest.getListName() + "'>" +
"<Method ID='1' Cmd='Delete'>" +
"<Field Name='ID'>5</Field>" +//this must be where we specify the file
"</Method>" +
"</Batch>"
));
I'm then making a method call on the listSoap object like this:
UpdateListItemsResult updateResult = listSoap.updateListItems("<my list name here>", updates);
I've also tried many variations like using the GUID instead of the actual list name and using
<Field Name='FileRef'><my file url here></Field>
to identify the file.
Nothing seems to be working, and I'm not getting any useful feedback either.
The generateXmlNode method that I'm using looks like this:
protected Node generateXmlNode(String sXML) throws ParserConfigurationException, SAXException, IOException {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setValidating(false);
DocumentBuilder builder = factory.newDocumentBuilder();
Document documentOptions = builder.parse(new InputSource(new StringReader(sXML)));
Node elementOptions = documentOptions.getDocumentElement();
return elementOptions;
}
but I've used this in the past when retrieving sharepoint lists without problems.
What am I missing here?
An other solution is to use HTTP DELETE method to delete a file directly to the Sharepoint with NTLMv2 authentication
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 DELETE 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 HttpDelete("http://[server]/[site]/[folder]/[fileName]");
return httpclient.execute(request);
} catch (IOException ex) {
//...
}
Linked question to upload a file to Sharepoint : How do I upload a document to SharePoint with Java?
You can take a look of this project i've developed to provide a working sharepoint rest api client usable in a very easy way. Take a look in github here:
https://github.com/kikovalle/PLGSharepointRestAPI-java
I am trying to create a standalone client to consume some web services. I must add my username and password to the SOAP Header. I tried adding the credentials as follows:
OTSWebSvcsService service = new OTSWebSvcsService();
OTSWebSvcs port = service.getOTSWebSvcs();
BindingProvider prov = (BindingProvider)port;
prov.getRequestContext().put(BindingProvider.USERNAME_PROPERTY, "myusername");
prov.getRequestContext().put(BindingProvider.PASSWORD_PROPERTY, "mypassword");
...
When I call a method on the service I get the following exception:
com.ibm.wsspi.wssecurity.SoapSecurityException: WSEC5048E: One of "SOAP Header" elements required.
What am I doing wrong? How would I add these properties to the SOAP Header?
Edited: I was using JAX-WS 2.1 included in JDK6. I am now using JAX-WS 2.2. I now get the following exception:
com.ibm.wsspi.wssecurity.SoapSecurityException: WSEC5509E: A security token whose type is [http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#UsernameToken] is required.
How do I go about creating this token?
Data can be transferred in SOAP header (JaxWS) by using #WebParam(header = true):
#WebMethod(operationName = "SendRequest", action = "http://abcd.ru/")
#Oneway
public void sendRequest(
#WebParam(name = "Message", targetNamespace = "http://abcd.ru/", partName = "Message")
Data message,
#WebParam(name = "ServiceHeader", targetNamespace = "http://abcd.ru/", header = true, partName = "ServiceHeader")
Header serviceHeader);
If you want to generate a client with SOAP Headers, you need to use -XadditionalHeaders:
wsimport -keep -Xnocompile -XadditionalHeaders -Xdebug http://12.34.56.78:8080/TestHeaders/somewsdl?wsdl -d /home/evgeny/DEVELOPMENT/JAVA/gen
If don't need #Oneway web service, you can use Holder:
#WebMethod(operationName = "SendRequest", action = "http://abcd.ru/")
public void sendRequest(
#WebParam(name = "Message", targetNamespace = "http://abcd.ru/", partName = "Message")
Data message,
#WebParam(name = "ServiceHeader", targetNamespace = "http://abcd.ru/", header = true, partName = "ServiceHeader")
Holder<Header> serviceHeader);
Not 100% sure as the question is missing some details but if you are using JAX-WS RI, then have a look at Adding SOAP headers when sending requests:
The portable way of doing this is that
you create a SOAPHandler and mess
with SAAJ, but the RI provides a
better way of doing this.
When you create a proxy or dispatch
object, they implement
BindingProvider interface. When you
use the JAX-WS RI, you can downcast to
WSBindingProvider which defines a
few more methods provided only by the
JAX-WS RI.
This interface lets you set an
arbitrary number of Header object,
each representing a SOAP header. You
can implement it on your own if you
want, but most likely you'd use one of
the factory methods defined on
Headers class to create one.
import com.sun.xml.ws.developer.WSBindingProvider;
HelloPort port = helloService.getHelloPort(); // or something like that...
WSBindingProvider bp = (WSBindingProvider)port;
bp.setOutboundHeader(
// simple string value as a header, like <simpleHeader>stringValue</simpleHeader>
Headers.create(new QName("simpleHeader"),"stringValue"),
// create a header from JAXB object
Headers.create(jaxbContext,myJaxbObject)
);
Update your code accordingly and try again. And if you're not using JAX-WS RI, please update your question and provide more context information.
Update: It appears that the web service you want to call is secured with WS-Security/UsernameTokens. This is a bit different from your initial question. Anyway, to configure your client to send usernames and passwords, I suggest to check the great post Implementing the WS-Security UsernameToken Profile for Metro-based web services (jump to step 4). Using NetBeans for this step might ease things a lot.
I'm adding this answer because none of the others worked for me.
I had to add a Header Handler to the Proxy:
import java.util.Set;
import java.util.TreeSet;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPFactory;
import javax.xml.soap.SOAPHeader;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;
public class SOAPHeaderHandler implements SOAPHandler<SOAPMessageContext> {
private final String authenticatedToken;
public SOAPHeaderHandler(String authenticatedToken) {
this.authenticatedToken = authenticatedToken;
}
public boolean handleMessage(SOAPMessageContext context) {
Boolean outboundProperty =
(Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (outboundProperty.booleanValue()) {
try {
SOAPEnvelope envelope = context.getMessage().getSOAPPart().getEnvelope();
SOAPFactory factory = SOAPFactory.newInstance();
String prefix = "urn";
String uri = "urn:xxxx";
SOAPElement securityElem =
factory.createElement("Element", prefix, uri);
SOAPElement tokenElem =
factory.createElement("Element2", prefix, uri);
tokenElem.addTextNode(authenticatedToken);
securityElem.addChildElement(tokenElem);
SOAPHeader header = envelope.addHeader();
header.addChildElement(securityElem);
} catch (Exception e) {
e.printStackTrace();
}
} else {
// inbound
}
return true;
}
public Set<QName> getHeaders() {
return new TreeSet();
}
public boolean handleFault(SOAPMessageContext context) {
return false;
}
public void close(MessageContext context) {
//
}
}
In the proxy, I just add the Handler:
BindingProvider bp =(BindingProvider)basicHttpBindingAuthentication;
bp.getBinding().getHandlerChain().add(new SOAPHeaderHandler(authenticatedToken));
bp.getBinding().getHandlerChain().add(new SOAPLoggingHandler());
Also, if you're using Maven to build your project, you'll need to add the following dependency:
<dependency>
<groupId>com.sun.xml.ws</groupId>
<artifactId>jaxws-rt</artifactId>
<version>{currentversion}/version>
</dependency>
This provides you with the class com.sun.xml.ws.developer.WSBindingProvider.
Link: https://mvnrepository.com/artifact/com.sun.xml.ws/jaxws-rt
you can add the username and password to the SOAP Header
BindingProvider prov = (BindingProvider)port;
prov.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY,
"your end point"));
Map<String, List<String>> headers = new HashMap<String, List<String>>();
prov.getRequestContext().put(BindingProvider.USERNAME_PROPERTY, "myusername");
prov.getRequestContext().put(BindingProvider.PASSWORD_PROPERTY, "mypassword");
prov.getRequestContext().put(MessageContext.HTTP_REQUEST_HEADERS, headers);
Use maven and the plugin jaxws-maven-plugin. this will generate a web service client. Make sure you are setting the xadditionalHeaders to true. This will generate methods with header inputs.
The best option (for my of course) is do it yourserfl. It means you can modify programattly all parts of the SOAP message
Binding binding = prov.getBinding();
List<Handler> handlerChain = binding.getHandlerChain();
handlerChain.add( new ModifyMessageHandler() );
binding.setHandlerChain( handlerChain );
And the ModifyMessageHandler source could be
#Override
public boolean handleMessage( SOAPMessageContext context )
{
SOAPMessage msg = context.getMessage();
try
{
SOAPEnvelope envelope = msg.getSOAPPart().getEnvelope();
SOAPHeader header = envelope.addHeader();
SOAPElement ele = header.addChildElement( new QName( "http://uri", "name_of_header" ) );
ele.addTextNode( "value_of_header" );
ele = header.addChildElement( new QName( "http://uri", "name_of_header" ) );
ele.addTextNode( "value_of_header" );
ele = header.addChildElement( new QName( "http://uri", "name_of_header" ) );
ele.addTextNode( "value_of_header" );
...
I hope this helps you
In jaxws-rt-2.2.10-ources.jar!\com\sun\xml\ws\transport\http\client\HttpTransportPipe.java:
public Packet process(Packet request) {
Map<String, List<String>> userHeaders = (Map<String, List<String>>) request.invocationProperties.get(MessageContext.HTTP_REQUEST_HEADERS);
if (userHeaders != null) {
reqHeaders.putAll(userHeaders);
So, Map<String, List<String>> from requestContext with key MessageContext.HTTP_REQUEST_HEADERS will be copied to SOAP headers.
Sample of Application Authentication with JAX-WS via headers
BindingProvider.USERNAME_PROPERTY and BindingProvider.PASSWORD_PROPERTY keys are processed special way in HttpTransportPipe.addBasicAuth(), adding standard basic authorization Authorization header.
See also Message Context in JAX-WS
I struggled with all the answers here, starting with Pascal's solution, which is getting harder with the Java compiler not binding against rt.jar by default any more (and using internal classes makes it specific to that runtime implementation).
The answer from edubriguenti brought me close. The way the handler is hooked up in the final bit of code didn't work for me, though - it was never called.
I ended up using a variation of his handler class, but wired it into the javax.xml.ws.Service instance like this:
Service service = Service.create(url, qname);
service.setHandlerResolver(
portInfo -> Collections.singletonList(new SOAPHeaderHandler(handlerArgs))
);
Adding an object to header we use the examples used here,yet i will complete
ObjectFactory objectFactory = new ObjectFactory();
CabeceraCR cabeceraCR =objectFactory.createCabeceraCR();
cabeceraCR.setUsuario("xxxxx");
cabeceraCR.setClave("xxxxx");
With object factory we create the object asked to pass on the header. The to add to the header
WSBindingProvider bp = (WSBindingProvider)wsXXXXXXSoap;
bp.setOutboundHeaders(
// Sets a simple string value as a header
Headers.create(jaxbContext,objectFactory.createCabeceraCR(cabeceraCR))
);
We used the WSBindingProvider to add the header. The object will have some error if used directly so we use the method
objectFactory.createCabeceraCR(cabeceraCR)
This method will create a JAXBElement like this on the object Factory
#XmlElementDecl(namespace = "http://www.creditreport.ec/", name = "CabeceraCR")
public JAXBElement<CabeceraCR> createCabeceraCR(CabeceraCR value) {
return new JAXBElement<CabeceraCR>(_CabeceraCR_QNAME, CabeceraCR.class, null, value);
}
And the jaxbContext we obtained like this:
jaxbContext = (JAXBRIContext) JAXBContext.newInstance(CabeceraCR.class.getPackage().getName());
This will add the object to the header.