utorak, 12. veljače 2008.

Java and Web Services Security: Axis2- part 1


For the first article in this series i have chosen Apache Axis2. Today, Axis2 is one of the most popular Java Web Service Api's, its battle tested, robust and as you will see quite simple to use. At the moment of writing of this article current version was 2.1.3, which is therefore the one used for building all examples you can find here. More information about Axis2 can be found at its homepage.

1. Environment and IDE setting up
First step is to download Axis2 binary distribution (here), after downloading it follow instructions at Axis2 web site on how to install it.
Web Services built with Axis2 can be run either in "Stand alone Axis2 Server" or within some Application Server, for my environment I have opted for the later and used Tomcat Application server (version 5.5.20). In this scenario, Axis2 function as a Java Web Application in which you deploy your web services as service jars, jars with axis2 specific structure and with extenstion ".aar".
Now its time to open up your favorite IDE and Set up a project. My favorite weapon of choice is MyEclipse IDE, whichis is derivate of popular Eclipse IDE. MyEclipse comes with lots of tools, many of which reassemble those usually find in eclipse WTP.

After building Axis2 war, as is described in installation instructions, instead of depolying to Application server i have created new WebProject in my IDE, and unzped war in its WebContent folder! This way I ended upa with a web application I could easily manage (add new, change existing files... ) through my IDE. After unzipping files and refreshing, my Project looked like this:

By no means I hold that the approach described above, is the recommended way to organize your Axis2 project. Everyone is entitled to its own way of doing things, but since there are several ways you can do this I though it to be good idea to share my approach in dealing with Axis2 and my Java IDE.
The main reason I have taken this approach is because I wanted to have Spring IoC capabilities available in my Web Services, of which more will be said later.

2. Service Creation
Creating service from WSDL is pretty simple with Axis2. First, take Shipping Service WSDL from initial post of this series of articles. Create new wsdl folder in your project and put shipping-service.wsdl file there. Then, open shell and position yourself in root folder of your project. Now execute following line:
%AXIS2_HOME%\bin\wsdl2java.bat -uri file:///{rootOfYourProject}/wsdl/shipping-services.wsdl -o {rootOfYourProject} -d adb -s -wv 1.1 -ss -sd -ssi
Do not forget to substitute value for {rootOfYourProject} with correct path of your own Project. If all goes well after wsdl2java script finishes its work your project should look like this:

As you can see wsdl2java script has generated, based on definitions from wsdl, Java files for both model and skeleton of your service. In addition it has also created folder resources/, containing necessary axis2 configuration files and build.xml with helper tasks used to build Axis2 service jar (aar).
Most important file to notice is: ShippingServiceSkeleton, that is file you should modify and put your bussines logic in it.
I have decided not to put bussines logic in it, but rather to create separate Java classes which implement bussines logic and is called from ShippingServiceSkeleton.
To do so I have created new simple interface:
ShippingOrderManager (which represents entry point to bussines implementation) and Dummy implementation of this interface (ShippingOrderManagerDummyImpl).

Following listing give content of both files:


package net.codah.sm.bussines;

import net.codah.sm.ws.schemas.ShippingOrderRequest;
import net.codah.sm.ws.schemas.ShippingOrderResponse;

public interface ShippingOrderManager {

ShippingOrderResponse processOrder(ShippingOrderRequest shippingRequest);
}

[ShippingOrderManager.java]

package net.codah.sm.bussines;

import java.math.BigDecimal;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import net.codah.sm.ws.schemas.AmmountType;
import net.codah.sm.ws.schemas.ShippingOrderRequest;
import net.codah.sm.ws.schemas.ShippingOrderResponse;

public class ShippingOrderManagerDummyImpl implements ShippingOrderManager {

protected final Log logger = LogFactory.getLog(ShippingOrderManager.class);

public ShippingOrderResponse processOrder(ShippingOrderRequest shippingOrderRequest) {
logger.debug("RECIEVED: " + shippingOrderRequest);
ShippingOrderResponse response = new ShippingOrderResponse();
response.setShippingId(new Long(System.currentTimeMillis()).toString());
response.setShippingAgencyId("SomeAgencyID");
AmmountType cost = new AmmountType();
cost.setAmmount(new BigDecimal(43.59));
cost.setCurrencyCode("EUR");
response.setCost(cost);

return response;
}

}

[ShippingOrderManagerDummyImpl]
And finally ShippingServiceSkeleton.java now look like this:


package net.codah.sm.ws.services;

import net.codah.sm.bussines.ShippingOrderManager;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
* ShippingServiceSkeleton java skeleton for the axisService
*/
public class ShippingServiceSkeleton implements ShippingServiceSkeletonInterface {

protected final Log logger = LogFactory.getLog(ShippingServiceSkeleton.class);

protected ShippingOrderManager shippingOrderManager;
/**
* Auto generated method signature
* @param shippingOrderRequest0
*/
public net.codah.sm.ws.schemas.ShippingOrderResponse processOrder(
net.codah.sm.ws.schemas.ShippingOrderRequest shippingOrderRequest0) {

return shippingOrderManager.processOrder(shippingOrderRequest0);
}

public void setShippingOrderManager(ShippingOrderManager shippingOrderManager) {
this.shippingOrderManager = shippingOrderManager;
}
}

As can be seen from the code above ShippingServiceSkeleton has setter for ShippingOrderManager, in order to wire it up with ShippingOrderManagerDummyImpl I have set up Axis2 to cooperate with spring.

3. Axis2 and Spring Configuration
Few years ago I have hooked up to Spring, and today can not any more imagine Java development without it. So I decided to integrate Spring IoC with my Axis2 Web Service. There are several ways how you can integrate Spring into Axis2, detailed instructions can be found here,
I have chosen the simples approach, one which relies on web.xml to load up Application Context over listener.
First put spring.jar file into WEB-INF/lib fodler, then modify web.xml file by adding following lines:


<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

Then create applicationContex.xml like the one bellow and put it to WEB-INF/ folder.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>

<!-- Axis2 Web Service -->
<bean id="shippingService" class="net.codah.sm.ws.services.ShippingServiceSkeleton">
<property name="shippingOrderManager" ref="shippingOrderManager"/>
</bean>

<bean id="shippingOrderManager" class="net.codah.sm.bussines.ShippingOrderManagerDummyImpl">
</bean>

</beans>

Now open file service.xml from resources folder. This is a file created by wsdl2java script and is used by Axis2 to describe the service. Now change it so it looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<!-- This file was auto-generated from WSDL -->
<!-- by the Apache Axis2 version: 1.3 Built on : Aug 10, 2007 (04:45:47 LKT) -->
<serviceGroup>
<service name="ShippingService">
<parameter name="ServiceObjectSupplier">org.apache.axis2.extensions.spring.receivers.SpringServletContextObjectSupplier</parameter>
<parameter name="SpringBeanName">shippingService</parameter>
<parameter name="useOriginalwsdl">true</parameter>
<parameter name="modifyUserWSDLPortAddress">true</parameter>
<operation name="processOrder" mep="http://www.w3.org/ns/wsdl/in-out">
<actionMapping>http://codah.net/ShippingOrderRequest</actionMapping>
<outputActionMapping>http://codah.net/sm/ws/services/ShippingServicesPortType/ShippingOrderResponse</outputActionMapping>
</operation>
<messageReceivers>
<messageReceiver mep="http://www.w3.org/ns/wsdl/in-out" class="net.codah.sm.ws.services.ShippingServiceMessageReceiverInOut"/>
</messageReceivers>
</service>
</serviceGroup>

Two key lines in the configuration above are:

<parameter name="ServiceObjectSupplier">org.apache.axis2.extensions.spring.receivers.SpringServletContextObjectSupplier</parameter>
<parameter name="SpringBeanName">shippingService</parameter>

which tell axis to take Service object from Spring Application context.

Now that we have Spring appliction context set up, and integrated with axis the only remaining thing to do is to change ant script that builds service aar file which is deployed in Axis2 Web Application, in WEB-INF/services/ folder.

4. Change Ant build
The problem is that default ant task "jar.server" which is used to create aar file will compile into it also the Java classes, which since they are in src/ folder now we do not need there. All we need in ShippingService.arr file now are the following files:
./ShippingService.aar
./META-INF
./META-INF/MANIFEST.MF
./META-INF/services.xml
To do so all you need to do is exclude java classes from jar creation prcess, locata and change following lines in build.xml file (jar.server task):

<jar destfile="${lib}/${name}.aar">
<fileset excludes="**/Test.class" dir="${classes}"/>
</jar>

so they would look like this:

<jar destfile="${lib}/${name}.aar">
<fileset excludes="**/*.class" dir="${classes}"/>
</jar>

Now execute ant task "jar.server" and drop newly created ShippingService.arr file from the build/lib/ folder to the WEB-INF/services/ folder and you should have fully functional service.
Now its time to add some security to this service, we will do this in next article of this series, until then...

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....