Adding authentication to an Apache CXF Web Service Client
In my last post, I provided steps for creating an Apache CXF Web Service Client. Now that client only works with Web Services that do not require you to authenticate. In this post, I will provide an example of how add authentication.
Step 1. Create a CXF Web Service client
Step 2. Create a CallbackHandler. The code below is actually from a previous post pertaining to authenticating an Axis 1.4 Web Service 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.commons.configuration.Configuration;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ws.security.WSPasswordCallback;
public class SamplePWCallback implements CallbackHandler {
private Configuration _config;
private static Log _logger = LogFactory.getLog(SamplePWCallback.class);
public SamplePWCallback () {
super();
_logger.info("Initializing Sample PW Callback");
_config = null;
try {
_config = new PropertiesConfiguration("sample.properties");
}
catch (ConfigurationException ce) {
_logger.error("Error loading configuration file", ce);
}
}
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 (_config.getString("username").equals(pc.getIdentifier())) {
pc.setPassword(_config.getString("password"));
}
}
else {
throw new UnsupportedCallbackException(callbacks[i], "Unrecognized Callback");
}
}
}
}
Step 3. Update your client to use your CallbackHandler. For the purposes of demonstration, let’s assume the global weather Web Service that I used in the previous blog post required authentication. The client would have to be updated to look similar to the sample one below:
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import net.webservicex.*;
import org.apache.cxf.binding.soap.saaj.SAAJOutInterceptor;
import org.apache.cxf.endpoint.Client;
import org.apache.cxf.endpoint.Endpoint;
import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.interceptor.LoggingInInterceptor;
import org.apache.cxf.interceptor.LoggingOutInterceptor;
import org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor;
import org.apache.ws.security.handler.WSHandlerConstants;
import org.apache.ws.security.WSConstants;
public class HelloCXF {
public static void main (String args[]) {
try {
GlobalWeather service = new GlobalWeather(new URL("file:globalweather.wsdl"));
GlobalWeatherSoap weatherSoap = service.getGlobalWeatherSoap();
Client client = ClientProxy.getClient( weatherSoap );
Endpoint endPoint = client.getEndpoint();
Map<String, Object> outProperties = new HashMap<String, Object>();
outProperties.put( WSHandlerConstants.ACTION,
WSHandlerConstants.USERNAME_TOKEN );
outProperties.put( WSHandlerConstants.USER, _config.getString("username"));
outProperties.put( WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_TEXT );
outProperties.put( WSHandlerConstants.PW_CALLBACK_CLASS,
SamplePWCallback.class.getName() );
WSS4JOutInterceptor wssOut = new WSS4JOutInterceptor( outProperties );
endPoint.getOutInterceptors().add( wssOut );
endPoint.getOutInterceptors().add( new SAAJOutInterceptor() );
endPoint.getInInterceptors().add( new LoggingInInterceptor() );
endPoint.getOutInterceptors().add( new LoggingOutInterceptor() );
System.out.println("Weather for New York");
System.out.println(weatherSoap.getWeather("New York", "United States"));
}
catch (MalformedURLException mue) {
System.err.println("problem with the wsdl url");
System.exit(1);
}
}
}
Creating an Apache CXF Web Service Client
So in early blog posts, I discussed how to make an Axis 1.4 client and secure it. Axis 1.4, however, has been end of lifed; no more work is being done on it. Now, if you are thinking about using Axis 2 to make a client in order to stick with a package that is actively being maintained, I would strongly advise against it. Axis 2 is really designed to be used on a Java application server and it is not friendly at all for standalone clients. There are literally 2 dozen jars plus modules that you need to add to your classpath if you want your client to work.
Fortunately, there is another Apache Web Service project, CXF. From what I can see, CXF is really a continuation of where Axis 1.4 left off. Even for Java Web apps, CXF is much friendlier to use than Axis 2.
Below is an example of how to setup a CXF client
Step 1. Download CXF
Step 2. Download the wsdl that you want to use. For this post, similar to the previous ones, I am going to use the global weather wsdl
Step 3. Use the wsdl2java CXF utility to generate Java code representing the global weather Web Service. The command should look like the following:
wsdl2java globalweather.wsdl
Step 4. Copy the code from step 3 into your source code directory. If you are working in Eclipse, you can drag and drop the code into your src directory
Step 5. Add the following files from the CXF lib directory to your classpath. If you are working in Eclipse, you can drag and drop these files into your own lib directory
- commons-collections-3.2.1.jar
- commons-lang-2.4.jar
- commons-logging-1.1.1.jar
- cxf-2.2.12.jar
- geronimo-activation_1.1_spec-1.0.2.jar
- geronimo-annotation_1.0_spec-1.1.1.jar
- geronimo-javamail_1.4_spec-1.6.jar
- geronimo-jaxws_2.1_spec-1.0.jar
- geronimo-servlet_2.5_spec-1.2.jar
- geronimo-stax-api_1.0_spec-1.0.1.jar
- geronimo-ws-metadata_2.0_spec-1.1.2.jar
- jaxb-api-2.1.jar
- jaxb-impl-2.1.13.jar
- neethi-2.0.4.jar
- saaj-api-1.3.jar
- saaj-impl-1.3.2.jar
- wsdl4j-1.6.2.jar
- wss4j-1.5.10.jar
- wstx-asl-3.2.9.jar
- xml-resolver-1.2.jar
- XmlSchema-1.4.7.jar
- xmlsec-1.4.3.jar
Step 6. Create a Web Service client. Below is an example of a client that works with the global weather Web Service:
import java.net.MalformedURLException;
import java.net.URL;
import net.webservicex.*;
public class HelloCXF {
public static void main (String args[]) {
try {
GlobalWeather service = new GlobalWeather(new URL("globalweather.wsdl"));
GlobalWeatherSoap weatherSoap = service.getGlobalWeatherSoap();
System.out.println("Weather for New York");
System.out.println(weatherSoap.getWeather("New York", "United States"));
}
catch (MalformedURLException mue) {
System.err.println("problem with the wsdl url");
System.exit(1);
}
}
}