WSS4J 1.5.x can be used for securing web
services deployed in virtually any application server, but it includes
special support for Axis. WSS4J 1.5.x ships with handlers that can be used in
Axis-based web
services for an easy integration. These handlers can be added to the
service deployment
descriptor (wsdd file) to add a WS-Security layer to the web service.
This is a step by step tutorial for deploying a simple service with
Username Token.
Prerequisites
Axis
1.2 installed and configured on a Tomcat Server. This
tutorial was performed on a Linux machine with Tomcat 5.5.4/Java 1.5.0,
however, the setup should be similar on other application servers, or
other operating systems (like Windows) unless we stated otherwise.
Steps
Installing WSS4J
Download the WSS4J binaries or build it from sources
Copy the contents of the WSS4J lib directory to your Axis
WEB-INF/lib directory. Many jar files will already exist. Most of them
will already exist there but you can just overwrite them all.
You may need to restart Tomcat unless you have automatic
deployment/class loading turned on. Check the Axis Happiness Page
(typically at http://localhost:8080/axis), make sure that the XML
Security (xmlsec.jar) is listed under the "Optional Components" section.
Creating the Service
This tutorial will secure the StockQuoteService which ships
with Axis. Unless you have one already, create a deployment descriptor
(deploy.wsdd) file with the following contents:
Use WSDL2Java to generate the client service bindings: java
org.apache.axis.wsdl.WSDL2Java -o .
-Nhttp://fox:8080/axis/services/stock-wss-01 samples.stock.client
http://fox:8080/axis/services/stock-wss-01?wsdl
A bunch of java classes will be created under samples/stock/client,
including the StockQuoteServiceServiceLocator.
Write a simple java client that uses the generated service
locator. For example:
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); } }
run the client: java
samples.stock.client.StockServiceClient IBM
If all went well, you should get the result:
stock quote service returned IBM: 95.68
Username Token
Configuring the Service
Modify the deployment descriptor you created above to look like
this: <deployment
xmlns="http://xml.apache.org/axis/wsdd/"
xmlns:java="http://xml.apache.org/axis/wsdd/providers/java"> <service
name="stock-wss-01" provider="java:RPC" style="document"
use="literal">
<requestFlow>
<handler type="java:org.apache.ws.axis.security.WSDoAllReceiver">
<parameter name="passwordCallbackClass" value="PWCallback"/>
<parameter name="action" value="UsernameToken"/>
</handler>
</requestFlow> <parameter
name="className" value="samples.stock.StockQuoteService"/> <parameter
name="allowedMethods" value="getQuote"/> <parameter
name="scope" value="application"/> </service> </deployment>
WSDoAllReceiver is an Axis handler that can be located in
wss4j.jar. This is the standard way to deploy an Axis handler. For more
details please refer to the Axis handler for WSS4J documentation.
Create a class named PWCallback.java and compile it into your
Axis WEB-INF/classes directory. In this example I used the default
package for simplicity, but you might need to use the fully qualified
class name (be consistent
with the deployment descriptor).
The following code snippet shows a simple password callback class:
public class PWCallback
implements CallbackHandler { public void
handle(Callback[] callbacks)
throws IOException, UnsupportedCallbackException {
for (int i = 0; i < callbacks.length; i++) {
if (callbacks[i] instanceof WSPasswordCallback) {
WSPasswordCallback pc = (WSPasswordCallback)callbacks[i];
// set the password given a username if
("wss4j".equals(pc.getIdentifier())) {
pc.setPassword("security");
}
} else {
throw new UnsupportedCallbackException(callbacks[i], "Unrecognized Callback");
}
} } }
Redeploy the service. Your service should now be expecting a WSS
Username Token in in the incoming requests, and clients should send the
username "wss4j" and password "security" to get through.
Configuring the Client
run the client we created again: java
samples.stock.client.StockServiceClient IBM
You should now get an error:
Exception in thread "main"
AxisFault faultCode:
{http://schemas.xmlsoap.org/soap/envelope/}Server.generalException faultSubcode: faultString:
WSDoAllReceiver: Request does not contain required Security header
This is because your client is not configured to send a Username Token
yet, so the service is rejecting the request.
Create a deployment descriptor file (client_deploy.wsdd) for the
client:
/** * PWCallback for the
Client */ public class PWCallback
implements CallbackHandler {
/** *
@see
javax.security.auth.callback.CallbackHandler#handle(javax.security.auth.callback.Callback[]) */ public void
handle(Callback[] callbacks) throws IOException,
UnsupportedCallbackException {
for (int i = 0; i < callbacks.length; i++) {
if (callbacks[i] instanceof WSPasswordCallback) {
WSPasswordCallback pc = (WSPasswordCallback)callbacks[i];
// set the password given a username
if ("wss4j".equals(pc.getIdentifier())) {
pc.setPassword("security");
}
} else {
throw new UnsupportedCallbackException(callbacks[i], "Unrecognized
Callback");
}
} } }
Define the system property axis.ClientConfigFile for your client:
EngineConfiguration config =
new FileProvider("client_deploy.wsdd"); StockQuoteServiceService
locator = new StockQuoteServiceServiceLocator(config); ...
Run the client, you should get no errors:
stock quote service returned
IBM: 95.7
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).
Another way to do this is to have the client application set the
username and CallbackHandler implementation programmatically instead of
client_deploy.wsdd: