CXF WS-Security using JSR 181 + Interceptor Annotations (XFire Migration)
Posted by Arsenalist on 31st July 2007
Posted in cxf, java, maven, spring, tech, web services, xfire | Comments Off
Posted by Arsenalist on 31st July 2007
Posted in cxf, java, maven, spring, tech, web services, xfire | Comments Off
Posted by Arsenalist on 27th January 2007
Hopefully this entry serves as some decent documentation on how to write a Python client that accesses a web service which uses WS-Security. When I was trying to figure it out, Otu Ekanem’s response on the mailing list was invaluable. The example is relevant for any web service framework independent of programming language. This is tested with XFire 1.2.4 but can be used with .NET or other Java web service frameworks like Axis2.
When accessing a web service which has WS-Security enabled you must send very specific headers as part of your SOAP envelope in order for the request to be processed. You can read all about the glorious specification in PDF Format if you like. I’m using the Zolera Soap Infrastructure (ZSI) Library for Python which supports client stub generation. Given the generated stubs, there are two ways of adding custom headers to outgoing SOAP messages.
Method 1 – Not desirable but worth a mention
The first method involves modifying the generated code which is highly undesirable. Using the very simple SportsService web service example, you must modify the generated SportsService_client.py and edit the following line:
[sourcecode language='python']
self.binding.Send(None, None,
request, soapaction=”", **kw)
[/sourcecode]
to read
[sourcecode language='python']
self.binding.Send(None, None,
request, soapaction=”", soapheaders=(obj1,obj2) )
[/sourcecode]
where obj1 and obj2 are instances of Python objects which are serialized as part of the SOAP header. I found this way to be tedious as you have to design your classes to match the SOAP header and write additional serialization code. It is also hard to create the exact header as namespaces and prefixes tend to be a problem.
Method 2 – Probably the way to go, way more customizable
We can use DOM-like methods to modify the SOAP header and send out exactly what we need. The example implements the UsernameToken strategy but other ones can also be implemented by modifying the headers in a similar manner. The generated Port class’ binding attribute has a sig_handler attribute which can be assigned an instance of a custom class. In this custom class, we must implement two methods, sign and verify, that can modify the header and check it’s validity, respectively. The sign method takes in as argument a SoapWriter which enables us to modify the header. So without further ado, here’s the class that adds WS-Security headers to the outgoing SOAP envelope as discussed above. The code has been formatted and modified to fit the page.
[sourcecode language='python']
# Deprecated in 2.5, use the hashlib module instead:
# http://docs.python.org/lib/module-hashlib.html
import sha
import binascii
import base64
import time
import random
class SignatureHandler:
OASIS_PREFIX =
“http://docs.oasis-open.org/wss/2004/01/” +
“oasis-200401″
SEC_NS = OASIS_PREFIX +
“-wss-wssecurity-secext-1.0.xsd”
UTIL_NS = OASIS_PREFIX +
“-wss-wssecurity-utility-1.0.xsd”
PASSWORD_DIGEST_TYPE = OASIS_PREFIX +
“-wss-username-token-profile-1.0#PasswordDigest”
PASSWORD_PLAIN_TYPE = OASIS_PREFIX +
“-wss-username-token-profile-1.0#PasswordText”
def __init__(self, user, password, useDigest=False):
self._user = user
self._created = time.strftime(‘%Y-%m-%dT%H:%M:%SZ’,
time.gmtime(time.time()))
self._nonce = sha.new(str(random.random())).
digest()
if (useDigest):
self._passwordType = self.PASSWORD_DIGEST_TYPE
digest = sha.new(self._nonce + self._created +
password).digest()
# binascii.b2a_base64 adds a newline at the end
self._password = binascii.b2a_base64(digest)[:-1]
else:
self._passwordType = self.PASSWORD_PLAIN_TYPE
self._password = password
def sign(self,soapWriter):
# create element
securityElem = soapWriter._header.
createAppendElement(“”, “wsse:Security”)
securityElem.node.
setAttribute(“xmlns:wsse”, self.SEC_NS)
securityElem.node.
setAttribute(“SOAP-ENV:mustunderstand”, “1″)
# create element
usernameTokenElem = securityElem.
createAppendElement(“”, “wsse:UsernameToken”)
usernameTokenElem.node.
setAttribute(“xmlns:wsse”, self.SEC_NS)
usernameTokenElem.node.
setAttribute(“xmlns:wsu”, self.UTIL_NS)
# create element
usernameElem = usernameTokenElem.
createAppendElement(“”, “wsse:Username”)
usernameElem.node.
setAttribute(“xmlns:wsse”, self.SEC_NS)
# create element
passwordElem = usernameTokenElem.
createAppendElement(“”, “wsse:Password”)
passwordElem.node.
setAttribute(“xmlns:wsse”, self.SEC_NS)
passwordElem.node.
setAttribute(“Type”, self._passwordType)
# create element
nonceElem = usernameTokenElem.
createAppendElement(“”, “wsse:Nonce”)
nonceElem.node.
setAttribute(“xmlns:wsse”, self.SEC_NS)
# create element
createdElem = usernameTokenElem.
createAppendElement(“”, “wsse:Created”)
createdElem.node.
setAttribute(“xmlns:wsse”, self.UTIL_NS)
# put values in elements
usernameElem.
createAppendTextNode(self._user)
passwordElem.
createAppendTextNode(self._password)
# binascii.b2a_base64 adds a newline at the end
nonceElem.
createAppendTextNode(
binascii.b2a_base64(self._nonce)[:-1])
createdElem.createAppendTextNode(self._created)
def verify(self,soapWriter):
self
[/sourcecode]
Example usage of this is:
[sourcecode language='python']
from SportsService_client import *
from SportsService_types import *
locator = SportsServiceLocator()
port = locator.getSportsServiceHttpPort()
sigHandler = SignatureHandler(“user”, “password”, True)
port.binding.sig_handler = sigHandler
request = getMascotRequest()
teamObj = ns0.Team_Def(“Team”)
teamObj._name = “toronto”
request._team = teamObj
response = port.getMascot(request)
print response._out._name
[/sourcecode]
As you can see the SignatureHandler class is implementing an “interface” which enables it to process outgoing SOAP Requests. The verify method is empty but can contain code to check whether the SOAP header is valid.
If you would like write a PHP client that accesses a WS-Security enabled service, you should read Kim Cameron’s IdentityBlog entry which has links to the source code needed. If you simply want to use a PHP client for a non WS-Security web service, an earlier blog entry covers that.
| Tweet |
Posted in python, tech, web services | 17 Comments »
Posted by Arsenalist on 19th January 2007
If you’re finding yourself hunting down incomplete documentation about writing a PHP client for your Java or .NET web services, you’re not alone. PHP SOAP toolkits like NuSOAP and Pear SOAP provide sub-par API’s into web service development and give a decent person a really hard time when it comes to writing clients. The best way to go IMHO is using PHP’s built-in SOAP libraries. The other thing to keep your eye on is Axis2 for PHP which is still in Beta. For now we’ll focus on the PHP 5 and it’s SOAP extension. To enable the extension, your php.ini file must something like the following:
Under Dynamic Extensions:
extension_dir="C:/php/ext/" extension=php_soap.dll
Under Module Settings:
[soap] soap.wsdl_cache_enabled=0 soap.wsdl_cache_dir="/tmp" soap.wsdl_cache_ttl=86400
Restart the Apache Web Server for the changes to take effect, once that is done, we are free to use the PHP Soap extensions. This example is applicable to Java web services generated by XFire or Axis or any other framework and also to .NET web services. As long as there’s a WSDL, we’re good. This example has been tested with XFire 1.2.4.
In this example there is one web service called SportsService which exposes one method: public Mascot getMascot(Team team)
The method takes in as parameter a complex Java object called Team and returns a Mascot, another complex data type. Although this example can easily use primitives, I’ve chosen to use Java objects because I want to illustrate a specific point. The XFire Java client is fairly easy to write and a previous blog entry covered a similar client. Both the Team and Mascot classes are POJO’s with one String property called name, a default constructor and getter/setter methods. They are excluded here for brevity.
Before we look at the PHP client, let’s look at the relevant portion of the WSDL that is generated, mainly the types and the information about the method getMascot which we will be calling:
<wsdl:types>
<xsd:schema
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
attributeFormDefault="qualified"
elementFormDefault="qualified"
targetNamespace="http://vo.arsenalist.com">
<xsd:complexType name="Team">
<xsd:sequence>
<xsd:element minOccurs="0"
name="name"
nillable="true"
type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="Mascot">
<xsd:sequence>
<xsd:element minOccurs="0"
name="name"
nillable="true"
type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
</xsd:schema>
<xsd:schema
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
attributeFormDefault="qualified"
elementFormDefault="qualified"
targetNamespace="http://ws.arsenalist.com">
<xsd:element name="getMascot">
<xsd:complexType>
<xsd:sequence>
<xsd:element maxOccurs="1"
minOccurs="1"
name="team"
nillable="true"
type="ns1:Team"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="getMascotResponse">
<xsd:complexType>
<xsd:sequence>
<xsd:element maxOccurs="1"
minOccurs="1"
name="out"
nillable="true"
type="ns1:Mascot"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
</wsdl:types>
Here’s the PHP client that creates a Team type, passes it to the web service which returns a Mascot type.
<?php
//set up the service client using WSDL
$wsdl = "http://arsenalist.com/SportsService?wsdl";
$client = new SoapClient($wsdl);
// assign the name attribute of Team to "toronto"
$wrapper->team->name =
new SoapVar("toronto", XSD_STRING);
// call the getMascot method
$response = $client->__soapCall("getMascot",
array($wrapper));
// print out the Mascot
print($response->out->name);
?>
The wrapper object is created out of the blue so as to pass it to the web service as the lone parameter encapsulating the Team data type.
By looking at the WSDL, we can determine how to “instantiate” Team and Mascot data types on the client side. For example, to create a Team data type and assign it’s name property to “toronto”, we simply refer to it using associative array syntax and both the Team object and it’s property name are created “on the fly”. There’s no mucking around with namespaces etc. We’re simply wrapping the primitives inside other PHP “objects” using the same syntax as associative arrays. You can go as deep as you want in the object hierarchy in PHP without having to worry about encountering nulls because if an array element being accessed doesn’t exist, it is created for you.
It’s also always a good idea to use the $client->__getTypes() and do a print_r(..) on the results to see what you are dealing with before you start communicating with a WSDL For example, the result of a print_r($client->__getTypes()) on the above WSDL returned the following:
Array (
[0] => struct Team { string name; }
[1] => struct Mascot { string name; }
[2] => struct getMascot { Team team; }
[3] => struct getMascotResponse { Mascot out; }
)
As you can see this information can be helpful when deciding how to initialize your client side variables.
Note that you can also use $client->getMascot($wrapper) instead of $client->__soapCall(“getMascot”, array($wrapper)). The latter is used mostly when passing extra parameters such as SOAP headers etc.
This is a fairly simple example which hopefully illustrated the point that accessing web services using PHP is not a daunting task, and most of all there are no additional libraries to install as long as you’re using PHP5 and up. If you would like write a PHP client that accesses a WS-Security enabled service, you should read Kim Cameron’s IdentityBlog entry which has links to the source code needed.
| Tweet |
Posted in java, php, web, web services | 9 Comments »