Wednesday, February 22, 2006

Using WSS4J with Axis

The material below is from the Apache axis site and another web site with the examples modified and in greater detail.

The example elaborated below is to secure the StockQuoteService that comes with the Axis samples.(AXIS_HOME\samples\stock).

Prerequisite :

1)Configure Axis on Tomcat

2)Download wss4j and add the jar in the class path
<a href="http://www.apache.org/dyn/closer.cgi/ws/wss4j/">http://www.apache.org/dyn/closer.cgi/ws/wss4j/</a>
3)Make sure that all the required axis jar files are in the class path.Also the jar file opensaml-1.1.jar is required in the class path.

The steps are outlined below :



1)Create a deployment descriptor (deploy.wsdd) with the below contents.Note that the username token is added.

<deployment xmlns="http://xml.apache.org/axis/wsdd/" java="http://xml.apache.org/axis/wsdd/providers/java"> </deployment>

<service name="stock-wss-01" provider="java:RPC" style="" use="literal"> </service>

<requestflow> </requestflow>
<handler type="java:org.apache.ws.axis.security.WSDoAllReceiver"> </handler>

<parameter name="passwordCallbackClass" value="PWCallback"> </parameter>
<p><parameter name="action" value="UsernameToken"> </parameter>


<parameter name="className" value="samples.stock.StockQuoteService"> </parameter>

<parameter name="allowedMethods" value="getQuote"> </parameter></p><p><parameter name="scope" value="application"> </parameter>

<parameter name="passwordCallbackClass" value="PWCallback">
</parameter>
The WSDoAllReceiver is an Axis handler located in wss4j.jar package. This is the standard way to deploy an Axis handler. For more details please refer to the Axis handler for WSS4J documentation.
2)Deploy the service (using AxisAdmin). java org.apache.axis.client.AdminClient -lhttp://localhost:8080/axis/services/AdminService deploy.wsdd

3)Create a class named PWCallback.java and compile it and put the resulting PWCallback.class file into your Axis WEB-INF/classes directory. (under the appropriate package - samples/stock/client)

The following code snippet shows a simple password callback class:
package samples.stock.client;

import java.io.IOException;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import org.apache.ws.security.WSPasswordCallback;

public class PWCallback implements CallbackHandler {
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
for (int i = 0; i < pc =" (WSPasswordCallback)callbacks[i];" href="http://localhost:8080/axis/services/stock-wss-01?wsdl">http://localhost:8080/axis/services/stock-wss-01?wsdl</a>

A bunch of java classes will be created under samples/stock/client, including the StockQuoteServiceServiceLocator.

5)Create a deployment descriptor file (client_deploy.wsdd) for the client: <deployment xmlns="http://xml.apache.org/axis/wsdd/" java="http://xml.apache.org/axis/wsdd/providers/java">
<transport name="http" pivot="java:org.apache.axis.transport.http.HTTPSender">
<requestflow><deployment xmlns="http://xml.apache.org/axis/wsdd/" java="http://xml.apache.org/axis/wsdd/providers/java">
<transport name="http" pivot="java:org.apache.axis.transport.http.HTTPSender">
<globalconfiguration>
<requestflow>
<handler type="java:org.apache.ws.axis.security.WSDoAllSender">
<parameter name="action" value="UsernameToken">
<parameter name="user" value="wss4j">
<parameter name="passwordCallbackClass" value="samples.stock.client.PWCallback">
<parameter name="passwordType" value="PasswordDigest">
</parameter>
</parameter>
</parameter>
</parameter>
<parameter name="passwordCallbackClass" value="samples.stock.client.PWCallback">
</parameter>
6)Write the below client class that invokes the service.

package samples.stock.client;

import java.rmi.RemoteException;
import javax.xml.rpc.ServiceException;

public class StockServiceClient {
public StockServiceClient() {
}
public static void main(String[] args) throws ServiceException, RemoteException {
if (args.length == 0) {
System.out.println("Usage:\njava StockServiceClient [symbol]");
return;
}
StockQuoteServiceService locator = new StockQuoteServiceServiceLocator();
StockQuoteService service = locator.getStockWss01();
float quote = service.getQuote(args[0]);
System.out.println("stock quote service returned " + args[0] + ": " + quote);
}
}


7)Define the system property axis.ClientConfigFile for your client:

java -Daxis.ClientConfigFile=client_deploy.wsdd -classpath $AXISCLASSPATH samples.stock.client.StockServiceClient

Make sure that your CLASSPATH includes the jar files under WEB-INF/lib.

Another way to do this is to specify the wsdd file in your StockServiceClient to the service locator programmatically:

...
import org.apache.axis.EngineConfiguration;
import org.apache.axis.configuration.FileProvider;
...

EngineConfiguration config = new FileProvider("client_deploy.wsdd");
StockQuoteServiceService locator = new StockQuoteServiceServiceLocator(config);
...

8)Run the client, you should get no errors:

java samples.stock.client.StockServiceClient XXX

stock quote service returned XXX: 55.25

Your client is now sending a Username Token in the wsse request header with the username "wss4j" (see client_deploy.wsdd) and password "security" (see the PWCallback implementation).


9)Try modifying your client's PWCallback to return the wrong password, or send the wrong username. The service should reject your requests.</handler></requestflow></globalconfiguration></transport></deployment></requestflow></transport></deployment>

2 comments:

Leonardo said...

Hi,
The article is very good. You can write a link for download the project source?

Thanks

leonorti

tiger said...

Thanks for the article,
I still got incompatible headers, had to do the ugly manual additions as below:

public static void setWSSEHeader(Call _call) throws SOAPException{
SOAPHeaderElement element =
new SOAPHeaderElement(Messages.getString("NSSvsUtils.SOAP_2003_06_NAMESPACE"),Messages.getString("NSSvsUtils.SECURITY_ELEMENT_NAME")); //$NON-NLS-1$ //$NON-NLS-2$
element.setMustUnderstand(true);
element.setPrefix(Messages.getString("NSSvsUtils.WSSE_ELEMENT_NAME")); //$NON-NLS-1$
element.setActor(null);

QName usernameQname = new QName(Messages.getString("NSSvsUtils.SOAP_2003_06_NAMESPACE"),Messages.getString("NSSvsUtils.USERNAME_ELEMENT_NAME"),Messages.getString("NSSvsUtils.WSSE_ELEMENT_NAME")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
MessageElement usernameElement = new MessageElement(usernameQname);
usernameElement.addTextNode("XXXX"); //$NON-NLS-1$

QName passwordQname = new QName(Messages.getString("NSSvsUtils.SOAP_2003_06_NAMESPACE"),"Password",Messages.getString("NSSvsUtils.WSSE_ELEMENT_NAME")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
MessageElement passwordElement = new MessageElement(passwordQname);
passwordElement.addTextNode("YYYY"); //$NON-NLS-1$


QName qname = new QName(Messages.getString("NSSvsUtils.SOAP_2003_06_NAMESPACE"),Messages.getString("NSSvsUtils.USERNAMTTOKEN_ELEMENT_NAME"),Messages.getString("NSSvsUtils.WSSE_ELEMENT_NAME")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
MessageElement element1 = new MessageElement(qname);
element1.addChild(usernameElement);
element1.addChild(passwordElement);
element.setObjectValue(element1);

_call.addHeader(element);

}