Feb 21
SOAP headers in Flex and WS-Security
Dear readers,
I just spent 2 entire days trying to figure out how to consume a SOAP Webservice that requires a SOAP-header and WS-Security in Flex. Hopefully this post will help you out if you're looking to do the same thing.
Let me start off by saying that the Import Web Service (WSDL) wizard in Flex 3 doesn't work as it should!
The generated classes return invalid XML to the SOAP-request, and adding headers to your request is completely neglected.
But I did get it to work using the classical <mx:Webservice> tag and some hand-coding.
Here's how to do it:
First you need a copy of the as3corelib (only if you need the WS-Security), more specifically you need the com.adobe.crypto.WSSEUsernameToken.as class.
This class returns a token as a String, which I think is not very convenient, so I added another function called "getUsernameTokenAsArray" which returns the different token elements required for WS-Security in an Array. This way you can just "pluck" the parts you need without having to parse a String.
Next you need to write some AS3 classes to map the datatypes expected by the WebService to your Flex app, and vice-versa.
I wrote a little utility class that lets you easily insert different types of SOAP-headers to your requests.
Currently methods are implemented for returning headers containing WS-Security. Feel free to use it and add your own methods for additional SOAP-header-types to it.
Here it is:
-
package be.svendens.util
-
{
-
import mx.rpc.soap.SOAPHeader;
-
import com.adobe.crypto.WSSEUsernameToken;
-
-
public class SOAPHeaderUtil
-
{
-
private static const WSSE_SECURITY:QName = new QName( "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", "Security" );
-
-
public static function returnWSSEHeaderWithNonceAndTimestamp(username:String, password:String, nonce:String=null, timestamp:Date=null):SOAPHeader
-
{
-
var userToken:String = "UsernameToken-"+Math.round(Math.random()*999999).toString();
-
var wsseToken:Array = WSSEUsernameToken.getUsernameTokenAsArray(username, password, nonce, timestamp);
-
var headerXML : XML = <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
-
<wsse:UsernameToken wsu:Id={userToken} xmlns:wsu='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd'>
-
<wsse:Username>{wsseToken[0]}</wsse:Username>
-
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">{wsseToken[1]}</wsse:Password>
-
<wsse:Nonce>{wsseToken[2]}</wsse:Nonce>
-
<wsu:Created>{wsseToken[3]}</wsu:Created>
-
</wsse:UsernameToken>
-
</wsse:Security>;
-
var header : SOAPHeader = new SOAPHeader( WSSE_SECURITY, headerXML );
-
return header;
-
}
-
-
public static function returnWSSEHeaderWithoutNonceAndTimestamp(username:String, password:String):SOAPHeader
-
{
-
var userToken:String = "UsernameToken-"+Math.round(Math.random()*999999).toString();
-
var wsseToken:Array = WSSEUsernameToken.getUsernameTokenAsArray(username, password);
-
var headerXML : XML = <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
-
<wsse:UsernameToken wsu:Id={userToken} xmlns:wsu='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd'>
-
<wsse:Username>{wsseToken[0]}</wsse:Username>
-
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">{wsseToken[1]}</wsse:Password>
-
</wsse:UsernameToken>
-
</wsse:Security>;
-
var header : SOAPHeader = new SOAPHeader( WSSE_SECURITY, headerXML );
-
return header;
-
}
-
}
-
}
Now you can call your webservice and add headers like this:
-
var header:SOAPHeader = SOAPHeaderUtil.returnWSSEHeaderWithNonceAndTimestamp("username", "password");
-
myService.addHeader(header);
Basically you create an <mx:Webservice>, pass it the correct parameters for your webservice. Then create a function to add the headers.
Call the clearHeaders method of your webservice (it's a default method for a webservice object) to clear any existing headers, next call the addHeader method to attach the new header.
Finally call your webservice operation with the necessary input parameters and there you go: a nice SOAP-envelope with headers and WS-Security!
You can download a complete demo containing all necessary classes and a sample MXML file here.
WSDL loaded
Invoking SOAP operation CreateCTAScheduleItem
Encoding SOAP request envelope
Encoding SOAP request body
'B5C37908-53AE-B85B-EDE1-3C3A54EB5255' producer sending message 'F9F59B36-51D7-DCC7-C673-3C3A59283DE9'
'direct_http_channel' channel sending message:
(mx.messaging.messages::SOAPMessage)#0
body = "<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SOAP-ENV:Header>
<wsse:Security SOAP-ENV: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-502651" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<wsse:Username>Flex</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">mA5XaNPovDGiotMyb/e8A2fiIbU=</wsse:Password>
<wsse:Nonce>MDU3NDc4OTIzODE2MjMzODc=</wsse:Nonce>
<wsu:Created>2008-02-21T14:41:39.251Z</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<schema:CreateCTAScheduleItemRequest xmlns:schema="http://yournamespace">
<timePeriod>
<StartDate>2008-02-21T13:41:38.654Z</StartDate>
<EndDate>2008-02-21T13:41:38.654Z</EndDate>
</timePeriod>
<priority>4</priority>
<cta>test</cta>
<applicationURL>http://blog.svendens.be</applicationURL>
<repeatInterval>2</repeatInterval>
</schema:CreateCTAScheduleItemRequest>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>"
I'm still struggling with the authentication however.
The SOAP-request looks exactly as it should and validates fine by the WSDL, but when using the Nonce and Created elements my username/digest and nonce keep getting rejected by the server. Without the nonce everything works like a charm.
Any of you guru's out there know how to tackle this last problem?


February 22nd, 2008 at 1:23 pm
[…] a SOAP-header and WS-Security in Flex?Check out this most helpful post written by Sven over at Tested Unit. Technorati Tags: flex, soap, ws […]
February 29th, 2008 at 10:50 pm
I too am having trouble with using the web services introspection wizard and the proxy classes it generates to set SOAP header authentication data.
I’m going to try your method, however I was really hoping to use the proxy classes generated by flex for my web service. Now I need to hand code all the stubs.
This is really burning me out.
Drop me a line if you’d like to chat.
Rob
March 3rd, 2008 at 4:19 pm
Hey Rob,
Yep, it sure s*cks :-s
Haven’t found a way yet to use the generated proxy classes (have not had the time to be exact, am working on a client project with a *VERY* tight deadline. My project should be finished by the end of the week, and then I’ll be doing some experiments to see if it’s possible to tweak the generated classes a bit so you could use them as they were meant to.
Will keep you posted of the progress on this blog!