ponedjeljak, 11. veljače 2008.

Java and Web Services Security: part 0

Prologue
Since my last post, I have been doing a lot of research, and playing with several available Java web services API-s, to see what are the benefits and goals of each in particular. In the process, I have checked out JAX-WS, Axis2 and Spring Web Services. Of the three I was quite disappointed by JAX-WS, not so much with the implementation (although I do not like annotations) but rather with the available documentation and working samples. Working with JAX-WS was like German advance on the Eastern Front, slow, painful and frustrating.... so I gave up on it and concentrated on the remaining two.
The goal I set up before my self, when working with each of the above API-'s was to develop simple Web Service using particular Java API and then to apply WS Security to it in the same way as I have done it in the CXF example, namely to add
UsernameToken and Timestamp token to the message header and finally to sign it. Other rule I decided to adher to was to ALWAYS use the "Top Down" or if you more like it "Contract first" approach in development of my web services. For those interested why I have decided to to so, there is a very good argumentation in favor of this approach on the Spring web services site. Furthermore I have decided to turn this (on going) private initiative into series of web articles all dedicated to Java Web Services Security.

The Service
The first thing I needed for my research was a specification of the web service I wanted to develop. I have decided to always work with the same Web Service, preferably one that has some resemblance with real life Web Services, and thus have devised the following Web Service, one used by virtual shipping company to receive shipping orders from various partners.
As its input Service accepts following data structures:

<shippingorderrequest>
<partnerid>PartnerId</partnerid>
<shippingdestination>
<fullname>FullName</fullname>
<city>City</city>
<zipcode>ZipCode</zipcode>
<country>Country</country>
<phone>Phone</phone>
</shippingdestination>
<orders>
<product>
<id>Id</id>
<name>Name</name>
<quantity>0</quantity>
<giftwrap>n</giftwrap>
<giftnote>Some text...</giftnote>
</product>
</orders>
<shippingmethod>STANDARD</shippingmethod>
</shippingorderrequest>

And after processing of the order (finding best transport company, nearest storage that contains ordered item etc.) it returns following data structure:


<ShippingOrderResponse>
<ShippingId>ShippingId</ShippingId>
<ShippingAgencyId>ShippingAgencyId</ShippingAgencyId>
<Cost>
<Ammount>59.99</Ammount>
<CurrencyCode>EUR</CurrencyCode>
</Cost>
</ShippingOrderResponse>

For the start I decieded that there should be only one operation called "processOrder" and that I will add other (orderStatus, cancelOrder etc...) later. Thus I wrote the shipping-service.wsdl file, which describes my simple service:

<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions xmlns:schema="http://codah.net/sm/ws/schemas"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:tns="http://codah.net/sm/ws/services"
targetNamespace="http://codah.net/sm/ws/services">

<wsdl:types>

<xs:schema elementFormDefault="qualified"
xmlns:sm="http://codah.net/sm/ws/schemas"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://codah.net/sm/ws/schemas">

<xs:element name="ShippingOrderRequest">
<xs:complexType>
<xs:all>
<xs:element name="PartnerId" type="xs:string" />
<xs:element name="ShippingDestination" type="sm:AddressType" />
<xs:element name="Orders">
<xs:complexType>
<xs:sequence minOccurs="1" maxOccurs="unbounded">
<xs:element name="Product" type="sm:ProductOrderType" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="ShippingMethod">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="STANDARD"/>
<xs:enumeration value="EXPEDITED"/>
<xs:enumeration value="PRIORITY"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
</xs:all>
</xs:complexType>
</xs:element>

<xs:element name="ShippingOrderResponse">
<xs:complexType>
<xs:all>
<xs:element name="ShippingId" type="xs:string" />
<xs:element name="ShippingAgencyId" type="xs:string" />
<xs:element name="Cost" type="sm:AmmountType" />
</xs:all>
</xs:complexType>
</xs:element>

<xs:complexType name="AddressType">
<xs:sequence>
<xs:element name="FullName" type="xs:string" />
<xs:element name="City" type="xs:string"/>
<xs:element name="State" type="xs:string" minOccurs="0"/>
<xs:element name="ZipCode" type="xs:string"/>
<xs:element name="Country" type="xs:string"/>
<xs:element name="Phone" type="xs:string"/>
</xs:sequence>
</xs:complexType>

<xs:complexType name="ProductOrderType">
<xs:sequence>
<xs:element name="Id" type="xs:string" />
<xs:element name="Name" type="xs:string" />
<xs:element name="Quantity" type="xs:integer" />
<xs:element name="GiftWrap" default="n" >
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:pattern value="[yn]"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="GiftNote" type="xs:string" minOccurs="0" />
</xs:sequence>
</xs:complexType>

<xs:complexType name="AmmountType">
<xs:sequence>
<xs:element name="Ammount" type="xs:decimal" />
<xs:element name="CurrencyCode" type="xs:string" />
</xs:sequence>
</xs:complexType>
</xs:schema>

</wsdl:types>

<!-- messages -->
<wsdl:message name="ShippingOrderRequest">
<wsdl:part element="schema:ShippingOrderRequest" name="ShippingOrderRequest" />
</wsdl:message>

<wsdl:message name="ShippingOrderResponse">
<wsdl:part element="schema:ShippingOrderResponse" name="ShippingOrderResponse" />
</wsdl:message>

<!-- portType -->
<wsdl:portType name="ShippingServicesPortType">
<wsdl:operation name="processOrder">
<wsdl:input message="tns:ShippingOrderRequest" name="ShippingOrderRequest" />
<wsdl:output message="tns:ShippingOrderResponse" name="ShippingOrderResponse" />
</wsdl:operation>
</wsdl:portType>

<!-- bindings -->
<wsdl:binding name="ShippingServicesBinding" type="tns:ShippingServicesPortType">
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" />
<wsdl:operation name="processOrder">
<soap:operation soapAction="http://codah.net/ShippingOrderRequest" />
<wsdl:input name="ShippingOrderRequest">
<soap:body use="literal" />
</wsdl:input>
<wsdl:output name="ShippingOrderResponse">
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
</wsdl:binding>

<!-- service -->
<wsdl:service name="ShippingService">
<wsdl:port binding="tns:ShippingServicesBinding" name="ShippingServicePort">
<soap:address location="http://localhost:8080/axis2/" />
</wsdl:port>
</wsdl:service>

</wsdl:definitions>
Above listing concludes this preparatory post, next one in series will be dealing with implementation of the above service with Axis2.

Until then....

Nema komentara: