Axis1 Deployment Tutorial

Introduction

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

  1. Download the WSS4J binaries or build it from sources

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

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


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

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

      <parameter name="allowedMethods" value="getQuote"/>

      <parameter name="scope" value="application"/>

     </service>

    </deployment>



  1. deploy the service (using AxisAdmin):
    java org.apache.axis.client.AdminClient -lhttp://localhost:8080/axis/services/AdminService deploy.wsdd

Creating the Client

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



  2. Write a simple java client that uses the generated service locator. For example:



    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);

        }

    }



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

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



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



    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 < 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");

                }

            }

        }

    }







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

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



  2. Create a deployment descriptor file (client_deploy.wsdd) for the client:



    <deployment xmlns="http://xml.apache.org/axis/wsdd/" xmlns: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"/>

        </handler>

       </requestFlow >

      </globalConfiguration >

    </deployment>




  3. Create the samples.stock.client.PWCallback 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;



    /**

     * 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");

                }

            }

        }

    }



  4. 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);

    ...



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



    ...

    import org.apache.axis.client.Stub;

    ...



    Remote remote = locator.getPort(StockQuoteService.class);

    Stub axisPort = (Stub)remote;

    axisPort._setProperty(UsernameToken.PASSWORD_TYPE, WSConstants.PASSWORD_DIGEST);

    axisPort._setProperty(WSHandlerConstants.USER, "wss4j");

    axisPort._setProperty(WSHandlerConstants.PW_CALLBACK_REF, pwCallback);



    where "pwCallback" is a reference to a PWCallback implementation. See the Axis handler for WSS4J documentation for more details on this.



  6. Try modifying your client's PWCallback to return the wrong password, or send the wrong username. The service should reject your requests.