Scott's Workblog

scott.bradley.wilson@gmail.com


attention!
This blog has moved! Go to my new blog!


April 11, 2005

How to... dynamically include a WS-Security header in Apache Axis with WSS4J

I struggled with this one recently, and couldn't find a great deal of documentation on it, so I thought I'd better write it up in case anyone else out there can make use of it!

The Problem: I have a client application in Java that uses Axis to talk to a number of web services. Some of them use signing and encryption and some don't. Yet the client-config.wsdd file only lets me declare WS-Security headers as part of "Global Configuration", not per-connection. So how do I do it?

The Solution: Dynamically configure the request handler in your code.

First you need to set up an engine configuration method in your implementation, like this:

	private EngineConfiguration createClientConfig() 
	{ 
		try{
			SimpleProvider clientConfig=new SimpleProvider(); 
			Handler securityHandler= (Handler)new WSDoAllSender();
			securityHandler.setOption(WSHandlerConstants.ACTION,this.WSS_ACTION);
			securityHandler.setOption(WSHandlerConstants.SIG_PROP_FILE,this.WSS_SIG_PROPERTIES);
			securityHandler.setOption(WSHandlerConstants.USER, this.WSS_USER);
			securityHandler.setOption(WSHandlerConstants.PW_CALLBACK_CLASS, this.WSS_CALLBACK);
			SimpleChain reqHandler=new SimpleChain(); 
			SimpleChain respHandler=new SimpleChain(); 
			// add the handler to the request
			reqHandler.addHandler(securityHandler); 
			// add the handler to the response
			respHandler.addHandler(securityHandler); 
			Handler pivot=(Handler)new HTTPSender(); 
			Handler transport=new SimpleTargetedChain(reqHandler, pivot, respHandler); 
			clientConfig.deployTransport(HTTPTransport.DEFAULT_TRANSPORT_NAME,transport); 
			return clientConfig;   
		} catch (Exception ex){
			log.error("Couldn't create engine configuration",ex);
			return null;
		}
	} 

Note the constants in capitals; these are the values you want to set for the handler, and are equivalent to the values of the various XML tags you find in client-config.wsdd. So, WSS_ACTION should be set to "encrypt" or "sign" or whatever you want WSS4J to do, WSS_SIG_PROPERTIES is whatever your crypto.properties file is called, and so on.

Then, before invoking the service, call the createClientConfig() method to setup the Axis engine with the security handler:

                
SampleServiceLocator service=new SampleServiceLocator();	
if (this.WSS_ACTION != null){
    EngineConfiguration clientConfig=createClientConfig(); 
    service.setEngineConfiguration(clientConfig); 
    service.setEngine(new AxisClient(clientConfig));
}
SRWPort port=service.getSRW(this.target.getUrl());
SampleRequestType request=new SampleRequestType();

Thats pretty much it. You need to implement a callback class and set up the keystore for encrypting and signing as per usual. It would have been nice if we could configure this on a per-service basis in client-config.wsdd, but this is the next best thing. If nothing else, you can set all these values in a Properties file and load them at runtime, rather than hard-coding them as constants within the code.

main archive