I have a SOAP web service. When it is called, I return a response this way:
#PayloadRoot(namespace = NAMESPACE_URI, localPart = "...")
#ResponsePayload
public JAXBElement<MyResponseObject> mySoapService(#RequestPayload MyRequestObject request) {
MyResponseObject res = new MyResponseObject();
res.setField("my field content");
...
return createJaxbElementWithPrefix(res, MyResponseObject.class, "prefix");
}
Testing with SoapUI, the returned XML is
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<prefix:MyResponseObject xmlns:prefix="http://..url">
<field>my field content</field>
</prefix:MyResponseObject>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Unfortunately, for some reason that's beyond my control, the systems who calls my service, expects the prefix namespace in the envelope, so something like this:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:prefix="http://..url">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<prefix:MyResponseObject>
<field>my field content</field>
</prefix:MyResponseObject>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
This brings to my problem. I need to manually modify the Envelope, but I can't find a way. Could someone help me?
Related
I developed one spring SOAP web service that is up and running. However, the request id I'm passing from the request is not getting updated. For all the requests I'm getting request id as 0. please help me with this issue.
My Endpoint details.
package com.example.thirdsoapweb.ThirdSoapWebService.soap;
import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
import org.springframework.ws.server.endpoint.annotation.RequestPayload;
import org.springframework.ws.server.endpoint.annotation.ResponsePayload;
import com.in_28minutes.courses.CourseDetails;
import com.in_28minutes.courses.GetCourseDetailsRequest;
import com.in_28minutes.courses.GetCourseDetailsResponse;
#Endpoint
public class CourseDetailsEndpoint {
//endpoint
//reuestobject
//respopnseobject
// http://in_28minutes.com/courses
// GetCourseDetailsRequest
#PayloadRoot(namespace = "http://in_28minutes.com/courses", localPart = "GetCourseDetailsRequest")
#ResponsePayload
public GetCourseDetailsResponse processCurseDetailsRequest(#RequestPayload GetCourseDetailsRequest request)
{
GetCourseDetailsResponse response=new GetCourseDetailsResponse();
CourseDetails courseDetails=new CourseDetails();
courseDetails.setId(request.getId());
System.out.println(request.getId());// here only i'm getting 0
courseDetails.setName("Rakesh");
courseDetails.setDescription("Spring Microservices and for the first time");
response.setCourseDetails(courseDetails);
return response;
}
}
Request payload
<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/">
<Body>
<GetCourseDetailsRequest xmlns="http://in_28minutes.com/courses">
<id>[908]</id>
</GetCourseDetailsRequest>
</Body>
</Envelope>
Response payload
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<ns2:GetCourseDetailsResponse xmlns:ns2="http://in_28minutes.com/courses">
<ns2:CourseDetails>
<ns2:id>0</ns2:id>
<ns2:name>Rakesh</ns2:name>
<ns2:description>Spring Microservices and for the first time</ns2:description>
</ns2:CourseDetails>
</ns2:GetCourseDetailsResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
I tried debugging but cannot find out why I'm getting 0 in my request id. I want the requestid which I'm passing from the request.
I have an apache CXF client for a Microsoft WCF service, and I am attempting to send a file via MTOM. However, I keep getting a 400, and the error on the WCF side according to the partner is that there is an error creating the MTOM reader
I've traced the outbound message, and it looks like this:
INFO: Outbound Message
---------------------------
ID: 1
Address: https://someserver.com/ImportService.svc?wsdl
Encoding: UTF-8
Http-Method: POST
Content-Type: multipart/related; type="application/xop+xml"; boundary="uuid:1d46d7c9-047b-440d-928b-ab8689ab5e6f"; start="<root.message#cxf.apache.org>"; start-info="application/soap+xml; action=\"http://tempuri.org/IImportService/UploadFile\""
Headers: {Accept=[*/*], Accept-Encoding=[gzip;q=1.0, identity; q=0.5, *;q=0], Content-Encoding=[gzip]}
Payload: --uuid:1d46d7c9-047b-440d-928b-ab8689ab5e6f
Content-Type: application/xop+xml; charset=UTF-8; type="application/soap+xml; action=\"http://tempuri.org/IImportService/UploadFile\""
Content-Transfer-Encoding: binary
Content-ID: <root.message#cxf.apache.org>
<?xml version="1.0"?>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
<soap:Header>
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" soap:mustUnderstand="true">
<wsse:UsernameToken wsu:Id="UsernameToken-e51a6fdd-5053-4aae-a9fb-363dde7d9e77">
<wsse:Username>blah#test.com</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">mypassword</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
<ns2:letterOptions xmlns="http://schemas.datacontract.org/2004/07/PublicServices.Import" xmlns:ns2="http://tempuri.org/">
<EnableQBPlanConsolidation>false</EnableQBPlanConsolidation>
<MASKSSN>true</MASKSSN>
<SRPrintedNumberofDays>2</SRPrintedNumberofDays>
<SuppressAllLetters>false</SuppressAllLetters>
<SuppressNewMemberLoginLetter>false</SuppressNewMemberLoginLetter>
<SuppressTakeOverLetterForTermed>false</SuppressTakeOverLetterForTermed>
<SuppressTerminationLetter>false</SuppressTerminationLetter>
</ns2:letterOptions>
<ns2:JobQueueType xmlns="http://schemas.datacontract.org/2004/07/PublicServices.Import" xmlns:ns2="http://tempuri.org/">Import</ns2:JobQueueType>
<Filename xmlns="http://tempuri.org/">testImport.csv</Filename>
<Action xmlns="http://www.w3.org/2005/08/addressing">http://tempuri.org/IImportService/UploadFile</Action>
<MessageID xmlns="http://www.w3.org/2005/08/addressing">urn:uuid:f380e4cc-225f-4b7d-bd46-6b5d607a59ca</MessageID>
<To xmlns="http://www.w3.org/2005/08/addressing">https://someserver.com/ImportService.svc?wsdl</To>
<ReplyTo xmlns="http://www.w3.org/2005/08/addressing">
<Address>http://www.w3.org/2005/08/addressing/anonymous</Address>
</ReplyTo>
</soap:Header>
<soap:Body>
<FileUploadMessage xmlns="http://tempuri.org/" xmlns:ns2="http://schemas.datacontract.org/2004/07/PublicServices.Import" xmlns:ns3="http://schemas.microsoft.com/2003/10/Serialization/">
<FileByteStream>
<xop:Include xmlns:xop="http://www.w3.org/2004/08/xop/include" href="cid:68e0408d-81da-496b-a06c-24a0459207d1-1#tempuri.org"/>
</FileByteStream>
</FileUploadMessage>
</soap:Body>
</soap:Envelope>
--uuid:1d46d7c9-047b-440d-928b-ab8689ab5e6f
Content-Type: application/octet-stream
Content-Transfer-Encoding: binary
Content-ID: <68e0408d-81da-496b-a06c-24a0459207d1-1#tempuri.org>
[VERSION],1.0
[NPM],552652222,1,Basic Client,Basic Client,Bob,Z,Jones,MR,bjones#test.com,402444555,,1234 Some street,,Omaha,NE,68123,,M,T,F,F
--uuid:1d46d7c9-047b-440d-928b-ab8689ab5e6f--
I've found plenty of other instances where other folks had the same issue:
https://coderanch.com/t/224995/java/Apache-CXF-MTOM-enabled-WCF
HTTP Bad Request error when requesting a WCF service contract
http://mail-archives.apache.org/mod_mbox/cxf-users/201211.mbox/%3CCAPXLCrCLkSkC8dQFeuU8DLY6gne1SOhwT9eMDxAUxLudnqU+YA#mail.gmail.com%3E
None of these were able to resolve my issue. I've tried multiple different versions of CXF and I get the same error with all of them.
This is a consolidated version of the code that is calling the service:
JaxWsProxyFactoryBean proxyFactory = new JaxWsProxyFactoryBean();
proxyFactory.setBindingId(SOAPBinding.SOAP12HTTP_MTOM_BINDING);
proxyFactory.setServiceClass(IImportService.class);
proxyFactory.setAddress(proxyEndpoint);
proxyFactory.getFeatures().add(new WSAddressingFeature());
IImportService importService = (IImportService) proxyFactory.create();
Client client = (Client) importService;
LetterOptions letterOptions = new LetterOptions();
letterOptions.setSRPrintedNumberofDays(2);
letterOptions.setMASKSSN(true);
letterOptions.setEnableQBPlanConsolidation(false);
List<Object> headerList = new ArrayList<>();
headerList.add(new Header(new QName("http://tempuri.org/", "letterOptions"),
letterOptions, new JAXBDataBinding(LetterOptions.class)));
headerList.add(new Header(new QName("http://tempuri.org/", "JobQueueType"), JobQueueType.IMPORT, new JAXBDataBinding(JobQueueType.class)));
headerList.add(new Header(new QName("http://tempuri.org/", "Filename"), "testImport.csv", new JAXBDataBinding(String.class)));
client.getRequestContext().put(Header.HEADER_LIST, headerList);
client.getEndpoint().getActiveFeatures().add(new LoggingFeature());
client.getInInterceptors().add(new GZIPInInterceptor());
client.getInInterceptors().add(new LogResponseInterceptor());
GZIPOutInterceptor outInterceptor = new GZIPOutInterceptor();
outInterceptor.setForce(true);
client.getOutInterceptors().add(outInterceptor);
Map props = new HashMap();
props.put(WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN);
props.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_TEXT);
props.put(WSHandlerConstants.PW_CALLBACK_CLASS, PasswordCallbackHandler.class.getName());
props.put(WSHandlerConstants.USER, "blah#test.com");
WSS4JOutInterceptor wssOut = new WSS4JOutInterceptor(props);
client.getOutInterceptors().add(wssOut);
HTTPConduit conduit = (HTTPConduit) client.getConduit();
HTTPClientPolicy policy = conduit.getClient();
if(policy == null) {
policy = new HTTPClientPolicy();
}
policy.setAllowChunking(false);
FileUploadMessageReponse response = importService.uploadFile(fileUploadMessage);
One interesting tidbit is that I can copy the same request that is being logged into SoapUI, and it works fine.
I am having trouble getting WCF binding to work with Blackboard Java Web Services API.
(Simple answer would be if anyone has got this working could you
please post a working binding for WCF to Blackboard)
I have spent hours trying different configurations and custom coded bindings.
Some unsuccessful attempts:
calling-a-ws-security-java-web-service-with-c-sharp-client
wcf-client-with-ws-security 12-common-wcf-interop-confusions
configure-wcf-for-ws-security-with-username-over-https
wcf-client-connecting-to-java-soap-web-service-using-ws-security
ClearUsernameBinding
There are many more to do with JAVA and WS-Security with WCF but I wont go on.
It seems that every time I get one thing working another breaks. Now I feel like I am going around in circles and just making myself even more confused.
As my first test what I am trying to do is simple Initialize the Context object and Login using an Admin test user account with a WCF proxy.
Blackboard Doc ContextWS
To make sure that all of this worked I first I downloaded the sample code for .Net WSE 2.0 and tested that, it worked perfectly.
Now when I use WCF and binding I cannot get this same behaviour.
First the Successful exchange with very old WSE 2.0
===================================
WSE 2.0 ContextWS Initialization
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<soap:Header>
<wsa:Action>initialize</wsa:Action>
<wsa:MessageID>uuid:b975e989-a4ce-4e1e-abd6-500945346c40</wsa:MessageID>
<wsa:ReplyTo>
<wsa:Address>http://schemas.xmlsoap.org/ws/2004/03/addressing/role/anonymous</wsa:Address>
</wsa:ReplyTo>
<wsa:To>https://Blackboard.Server.Name/webapps/ws/services/Context.WS</wsa:To>
<wsse:Security soap:mustUnderstand="1">
<wsu:Timestamp wsu:Id="Timestamp-47d0d017-4fd1-46c2-b1b4-2431402cf847">
<wsu:Created>2015-07-16T04:58:02Z</wsu:Created>
<wsu:Expires>2015-07-16T05:03:02Z</wsu:Expires>
</wsu:Timestamp>
<wsse:UsernameToken xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="SecurityToken-1b71e23a-2d84-40a5-9509-b75902ec8b76">
<wsse:Username>session</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">nosession</wsse:Password>
<wsse:Nonce>lAW2qXrXZ1maNNkCEzlHGA==</wsse:Nonce>
<wsu:Created>2015-07-16T04:58:02Z</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>
</soap:Header>
<soap:Body />
</soap:Envelope>
WSE 2.0 ContextWS Initialization Success Response
<?xml version='1.0' encoding='utf-8'?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<ns:initializeResponse xmlns:ns="http://context.ws.blackboard">
<ns:return>c2762f357bbc42a4a88d33e4e42486b8</ns:return>
</ns:initializeResponse>
</soapenv:Body>
</soapenv:Envelope>
WSE 2.0 ContextWS Login Request
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<soap:Header>
<wsa:Action>login</wsa:Action>
<wsa:MessageID>uuid:a823128b-efb4-49e1-87d9-fd35167f0bfc</wsa:MessageID>
<wsa:ReplyTo>
<wsa:Address>http://schemas.xmlsoap.org/ws/2004/03/addressing/role/anonymous</wsa:Address>
</wsa:ReplyTo>
<wsa:To>https://Blackboard.Server.Name/webapps/ws/services/Context.WS</wsa:To>
<wsse:Security soap:mustUnderstand="1">
<wsu:Timestamp wsu:Id="Timestamp-c38daf19-6b39-4391-a3f8-bcc030064a3e">
<wsu:Created>2015-07-16T04:58:15Z</wsu:Created>
<wsu:Expires>2015-07-16T05:03:15Z</wsu:Expires>
</wsu:Timestamp>
<wsse:UsernameToken xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="SecurityToken-65948746-e616-436a-85f4-d2e1023e39be">
<wsse:Username>session</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">c2762f357bbc42a4a88d33e4e42486b8</wsse:Password>
<wsse:Nonce>T0xs8aiaiODMK3sfKgDQtg==</wsse:Nonce>
<wsu:Created>2015-07-16T04:58:15Z</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>
</soap:Header>
<soap:Body>
<login xmlns="http://context.ws.blackboard">
<userid>test_admin</userid>
<password>TestPassword</password>
<clientVendorId>TestClient</clientVendorId>
<clientProgramId>TestPOC</clientProgramId>
<loginExtraInfo xsi:nil="true" />
<expectedLifeSeconds>10000000</expectedLifeSeconds>
</login>
</soap:Body>
</soap:Envelope>
WSE 2.0 ContextWS Login Success Response
<?xml version='1.0' encoding='utf-8'?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<ns:loginResponse xmlns:ns="http://context.ws.blackboard">
<ns:return>true</ns:return>
</ns:loginResponse>
</soapenv:Body>
</soapenv:Envelope>
===================================
So I know that this works to our environment and I know the user can login.
Using WCF I am able to get the Initialization working but then it looses the session. It does not put the Returned Session ID into the Password field for the next message. I have tried to do this manually of course; but I get an error stating the Password field is read only.
Now for my WCF Configuration and code that has gotten me closest to the above communication.
WCF App.Config Binding
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
</system.serviceModel>
<bindings>
<customBinding>
<binding name="WCFSoapInteropJavaWS" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" >
<textMessageEncoding messageVersion="Soap11" writeEncoding="utf-8" />
<security authenticationMode="UserNameOverTransport" enableUnsecuredResponse="true" allowSerializedSigningTokenOnReply="true"
messageSecurityVersion="WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10"
includeTimestamp="true" allowInsecureTransport="true" canRenewSecurityContextToken="false" >
</security>
<httpsTransport authenticationScheme="Anonymous" />
</binding>
</customBinding>
</bindings>
<client>
<endpoint
address="https://Blackboard.Server.Name:443/webapps/ws/services/Context.WS"
binding="customBinding" bindingConfiguration="WCFSoapInteropJavaWS"
contract="ContextWS.ContextWSPortType" name="Context.WCFSoapInteropJavaWS" />
</client>
</system.serviceModel>
</configuration>
WCF C# code
public bool testWrapper(String userId, String userPassword){
try
{
context = new ContextWrapper("Context.WCFSoapInteropJavaWS");
context.ClientCredentials.UserName.UserName = "session";
context.ClientCredentials.UserName.Password = "nosession";
context.initialize();
//context.ClientCredentials.UserName.Password = "886d935527944f94a3526288e39a555e"; // SessionGUID_HERE Throws a Read Only Error for Pasword
bool retval = context.login(userId, userPassword, vendorId, programId, null, expectedLife);
return retval;
}
catch (System.Exception e)
{
lastError = e;
return false;
}
}
This is what the SOAP communication looks like.
WCF ContextWS Initialization Request
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<s:Header>
<VsDebuggerCausalityData xmlns="http://schemas.microsoft.com/vstudio/diagnostics/servicemodelsink">uIDPo+FmveflwUtMgSATRu3Ht9EAAAAAmYVJsX+bhUeYcTDsFqFktkqe8xmMiA1MpXouaouXgJwACQAA</VsDebuggerCausalityData>
<o:Security s:mustUnderstand="1" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<u:Timestamp u:Id="_0">
<u:Created>2015-07-16T07:15:05.109Z</u:Created>
<u:Expires>2015-07-16T07:20:05.109Z</u:Expires>
</u:Timestamp>
<o:UsernameToken u:Id="uuid-1237f56c-7c68-4d40-a756-7ff2c19a3235-1">
<o:Username>session</o:Username>
<o:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">nosession</o:Password>
</o:UsernameToken>
</o:Security>
</s:Header>
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"/>
</s:Envelope>
WCF ContextWS Initialization Success Response
<?xml version='1.0' encoding='utf-8'?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<ns:initializeResponse xmlns:ns="http://context.ws.blackboard">
<ns:return>886d935527944f94a3526288e39a555e</ns:return>
</ns:initializeResponse>
</soapenv:Body>
</soapenv:Envelope>
WCF ContextWS Login Request
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<s:Header>
<VsDebuggerCausalityData xmlns="http://schemas.microsoft.com/vstudio/diagnostics/servicemodelsink">uIDPo+JmveflwUtMgSATRu3Ht9EAAAAAmYVJsX+bhUeYcTDsFqFktkqe8xmMiA1MpXouaouXgJwACQAA</VsDebuggerCausalityData>
<o:Security s:mustUnderstand="1" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<u:Timestamp u:Id="_0">
<u:Created>2015-07-16T07:15:14.033Z</u:Created>
<u:Expires>2015-07-16T07:20:14.033Z</u:Expires>
</u:Timestamp>
<o:UsernameToken u:Id="uuid-1237f56c-7c68-4d40-a756-7ff2c19a3235-1">
<o:Username>session</o:Username>
<o:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">nosession</o:Password>
</o:UsernameToken>
</o:Security>
</s:Header>
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<login xmlns="http://context.ws.blackboard">
<userid>Test_admin</userid>
<password>TestPassword</password>
<clientVendorId>TestClient</clientVendorId>
<clientProgramId>TestPOC</clientProgramId>
<loginExtraInfo xsi:nil="true"/>
<expectedLifeSeconds>10000000</expectedLifeSeconds>
</login>
</s:Body>
</s:Envelope>
WCF ContextWS Login Failed Response
<?xml version='1.0' encoding='utf-8'?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<soapenv:Fault>
<faultcode>soapenv:Server</faultcode>
<faultstring>[WSFW001]Invalid session</faultstring>
<detail />
</soapenv:Fault>
</soapenv:Body>
</soapenv:Envelope>
As you can see the session return id has not been added to the password field on the login request so there is a "Invalid Session:
It all seemed to be going so well.
In short if anyone knows how to achieve a binding from a WCF Client to the Blackboard Java Webservice API and example would be fantastic. Other wise I am hoping that someone else that knows more about WCF Bindings to Java than I do might be able to take a look at the above see where I am going wrong.
Any help anyone can give me to get this working would be much appreciated so thanks. I really am hoping it is just something silly that I am missing.
Sorry for such a long detailed question.
Thanks to heaps of reading and some useful samples I was able to get this working. Blackboard with WCF.
Thanks go to both:
Ajadex Lopez
http://www.isyourcode.com/2010/08/attaching-oasis-username-tokens-headers.html
Johnny Lockhart
"Incoming message does not contain required Security header"
BB is buggie so you might do best to search the forum for WCF to find the post
Sample class
using System;
using System.ServiceModel.Description;
using System.ServiceModel.Channels;
using System.ServiceModel.Dispatcher;
using System.ServiceModel;
using System.Xml;
using System.Security.Cryptography;
using System.Text;
namespace BBWcfWrapper
{
/// <summary>
/// Coupled with the additional classes below, allows for injecting the WS-Security header into a WCF Service call without requiring SSL on the server.
/// </summary>
/// <remarks>http://isyourcode.blogspot.com/2010/08/attaching-oasis-username-tokens-headers.html</remarks>
public class BBWSSecurityBehavior : IEndpointBehavior
{
public MessageInspector MessageInspector { get; set; }
public BBWSSecurityBehavior(MessageInspector messageInspector)
{
MessageInspector = messageInspector;
}
public void Validate(ServiceEndpoint endpoint)
{ }
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{ }
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
if (this.MessageInspector == null) throw new InvalidOperationException("Caller must supply ClientInspector.");
clientRuntime.MessageInspectors.Add(MessageInspector);
}
}
public class MessageInspector : IClientMessageInspector
{
public MessageHeader[] Headers { get; set; }
public MessageInspector(params MessageHeader[] headers)
{
Headers = headers;
}
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
if (Headers != null)
{
for (int i = Headers.Length - 1; i >= 0; i--)
{
request.Headers.Insert(0, Headers[i]);
}
}
return request;
}
public void AfterReceiveReply(ref Message reply, object correlationState)
{
}
}
public class SecurityHeader : MessageHeader
{
public string SystemUser { get; set; }
public string SystemPassword { get; set; }
public SecurityHeader(string systemUser, string systemPassword)
{
SystemUser = systemUser;
SystemPassword = systemPassword;
}
public override string Name
{
get { return "Security"; }
}
public override string Namespace
{
get { return "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"; }
}
protected override void OnWriteStartHeader(XmlDictionaryWriter writer, MessageVersion messageVersion)
{
writer.WriteStartElement("wsse", Name, Namespace);
writer.WriteXmlnsAttribute("wsse", Namespace);
writer.WriteAttributeString("soap", "mustUnderstand", Namespace, "1");
}
protected override void OnWriteHeaderContents(XmlDictionaryWriter writer, MessageVersion messageVersion)
{
WriteHeader(writer);
}
private void WriteHeader(XmlDictionaryWriter writer)
{
var createDate = DateTime.Now;
//Start Parent Elements
writer.WriteStartElement("wsu","Timestamp","http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd");
writer.WriteAttributeString("wsu","id","http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd","Timestamp-6557466");
#region Start Child Elements
writer.WriteStartElement("wsu", "Created", XmlConvert.ToString(createDate, "yyyy-MM-ddTHH:mm:sszzzzzz"));
writer.WriteEndElement(); //End Created
writer.WriteStartElement("wsu", "Expires", XmlConvert.ToString(createDate.AddDays(1), "yyyy-MM-ddTHH:mm:sszzzzzz"));
writer.WriteEndElement(); //End Expires
#endregion
writer.WriteEndElement(); //End Timestamp
//Start Parent Elements
writer.WriteStartElement("wsse", "UsernameToken", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
writer.WriteXmlnsAttribute("wsu", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd");
#region Start Child Elements
writer.WriteStartElement("wsse", "Username", null);
writer.WriteString(SystemUser);
writer.WriteEndElement();//End Username
writer.WriteStartElement("wsse", "Password", null);
writer.WriteAttributeString("Type", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText");
writer.WriteString(SystemPassword);
writer.WriteEndElement();//End Password
// unique Nonce value - encode with SHA-1 for 'randomness'
// in theory the nonce could just be the GUID by itself
// This is used to stop Replay attacks
writer.WriteStartElement("wsse", "Nonce", null);
writer.WriteString(GetSHA1String(Guid.NewGuid().ToString()));
writer.WriteEndElement();//Nonce
writer.WriteStartElement("wsu", "Created", null);
writer.WriteString(XmlConvert.ToString(createDate, "yyyy-MM-ddTHH:mm:sszzzzzz"));
writer.WriteEndElement(); //End Created
#endregion
writer.WriteEndElement();//End UsernameToken
writer.Flush();
}
protected string GetSHA1String(string phrase)
{
SHA1CryptoServiceProvider sha1Hasher = new SHA1CryptoServiceProvider();
byte[] hashedDataBytes = sha1Hasher.ComputeHash(Encoding.UTF8.GetBytes(phrase));
return Convert.ToBase64String(hashedDataBytes);
}
}
}
Example Use
calendar = new CalendarWrapper("Calendar.BB_WSSecurity_Binding");
//This adds a custom security Headder for WCF and Java WS-Security Interop
calendar.Endpoint.Behaviors.Add(new BBWSSecurityBehavior(new MessageInspector(BbWsAuth.SecurityHeader)));
calendar.initializeCalendarWS(false);
Simple Wrapper class
public class CalendarWrapper : CalendarWSPortTypeClient
{
public CalendarWrapper() : base() { }
public CalendarWrapper(string endpointConfigurationName) : base(endpointConfigurationName) { }
public CalendarWrapper(Binding binding, EndpointAddress remoteAddress) : base(binding, remoteAddress) { }
}
Config
<bindings>
<basicHttpsBinding>
<binding name="BB_WSSecurity_Binding" messageEncoding="Text" textEncoding="utf-8" maxReceivedMessageSize="4000000" >
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
</binding>
</basicHttpsBinding>
</bindings>
<client>
<endpoint address="https://xxxxxxxxxx/webapps/ws/services/Calendar.WS"
binding="basicHttpsBinding" bindingConfiguration="BB_WSSecurity_Binding"
contract="CalendarWS.CalendarWSPortType" name="Calendar.BB_WSSecurity_Binding" />
This is what my code is creating:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ws="http://example.com">
<soapenv:Header/>
<soapenv:Body>
<ws:isUserExists>
<userId>10</userId>
</ws:isUserExists>
</soapenv:Body>
</soapenv:Envelope>
In my soapenv:Envelope || xmlns:soapenv is coming twice
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
Now this is what I need:
here xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" is not present in the SOAP request
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/ " xmlns:ws="http://example.com">
<soapenv:Header/>
<soapenv:Body>
<ws:isUserExists>
<userId>10</userId>
</ws:isUserExists>
</soapenv:Body>
</soapenv:Envelope>
The java code which I am using to create the request is:
public static SOAPMessage createIsUserExistsSOAPRequest(User user) throws SOAPException
{
MessageFactory messageFactory =MessageFactory.newInstance();
SOAPMessage soapMessage = messageFactory.createMessage();
soapMessage.getSOAPHeader().setPrefix("soapenv");
SOAPPart soapPart = soapMessage.getSOAPPart();
// SOAP Envelope
SOAPEnvelope envelope = soapPart.getEnvelope();
envelope.setPrefix("soapenv");
envelope.addNamespaceDeclaration("ws","http://example.com");
// SOAP Body
SOAPBody soapBody = envelope.getBody();
soapBody.setPrefix("soapenv");
SOAPElement ensureUserExistsElem = soapBody.addChildElement("isUserExists", "ws");
SOAPElement userIdElem = ensureUserExistsElem.addChildElement("userId");
userIdElem.addTextNode(user.userId);
String authorization = new sun.misc.BASE64Encoder().encode(("someid#gmail.com").getBytes());
MimeHeaders hd = soapMessage.getMimeHeaders();
hd.addHeader("Authorization", "Basic " + authorization);
soapMessage.saveChanges();
return soapMessage;
}
Please help : what I am doing wrong?
In my soapenv:Envelope || xmlns:soapenv is coming twice
It does not appear twice. You're defining 2 prefixes that are bound to the same namespace.
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
Please help : what I am doing wrong?
Nothing. It does not matter if the namespace prefix is soapenv or SOAP-ENV or polar-bear-ninjas. As long as the prefix is bound to a namespace you are fully qualifying that XML element. Both XMLs are valid and it does not matter which prefix is used.
We are creating a webservice that came from a WSDL which the soap messagehas both a header and body in the request and response. How can you set this up to work with a Spring WS? I can not find any examples????
In WSDL
<wsdl11:message name="createCourseSectionRequest">
<wsdl11:part name="Parameters" element="tns:createCourseSectionRequest"/>
<wsdl11:part name="HeaderInfoParameters" element="tns:imsx_syncRequestHeaderInfo"/>
</wsdl11:message>
<wsdl11:message name="createCourseSectionResponse">
<wsdl11:part name="Response" element="tns:createCourseSectionResponse"/>
<wsdl11:part name="HeaderInfoResponse" element="tns:imsx_syncResponseHeaderInfo"/>
</wsdl11:message>
Endpoint
#PayloadRoot(localPart="CreateCourseSectionRequest", namespace="")
#ResponsePayload
public CreateCourseSectionResponse createCourseSection(CreateCourseSectionRequest req) {
//TODO
return null;
}
Example
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ims="http://www.imsglobal.org/services/lis/cmsv1p0/wsdl11/sync/imscms_v1p0">
<soapenv:Header>
<ims:imsx_syncRequestHeaderInfo>
<ims:imsx_version>?</ims:imsx_version>
<ims:imsx_messageIdentifier>?</ims:imsx_messageIdentifier>
<!--Optional:-->
<ims:imsx_sendingAgentIdentifier>?</ims:imsx_sendingAgentIdentifier>
</ims:imsx_syncRequestHeaderInfo>
</soapenv:Header>
<soapenv:Body>
.....
</soapenv:Body>
</soapenv:Envelope>
Here is the solution I came up with. It works, but I wish the header part of the Soap message was strongly typed. .NET takes care of this for you while in Java it looks like you have to do more work to replicate a strongly typed object for the header. At least I can access the request/response header of the envelope with MessageContext.
#PayloadRoot(localPart="readCourseSectionRequest", namespace="http://www.imsglobal.org/services/lis/cmsv1p0/wsdl11/sync/imscms_v1p0")
#ResponsePayload
public ReadCourseSectionResponse readCourseSection(#RequestPayload ReadCourseSectionRequest parameters, MessageContext messageContext) {
//SaajSoapMessage response = (SaajSoapMessage) messageContext.getResponse();
//SoapHeader header = response.getSoapHeader();
//header.addHeaderElement(new QName("http://www.w3.org/2005/08/addressing", "MessageID", parameters.getSourcedId()));
return new ReadCourseSectionResponse();
}