Monday, March 5, 2007

Securing Spring-WS Client with XWSS

Warning: this post is now obsolete. Spring-WS WS-Security now work on the client side as well.

I was really excited about the new release of Spring Web Services (M3) and its new client side API. I like the simplicity and the elegance of the template based approach which is already used in various parts of Spring. In this post, I will show you how I used XWSS to add WS-Security support to web services invocations.
I used XWSS 2.0 from jwsdp-2.0. I had to add xmlsec.jar from the jwsdp-shared/lib in order for the example to work.

Server Side

As a simple example, I will configure the web service in the Echo sample to require a wss user name token.
To add WS-Security support, we need to add a XwsSecurityInterceptor to spring-ws-servlet.xml. The call back handler is a SimplePasswordValidationCallbackHandler that expects a user named "cafe" and "babe" as a password.

<bean id="wsSecurityInterceptor" class="">
    <property name="policyConfiguration" value="classpath:wss-server-config.xml" />
    <property name="callbackHandlers">
            <bean id="passwordValidationHandler" class="">        
                <property name="users">
                        <prop key="cafe">babe</prop>
wsSecurityInterceptor has to be added to the list of interceptors of payloadMapping

<property name="interceptors">
        <ref local="loggingInterceptor" />
        <ref local="wsSecurityInterceptor" />
        <ref local="validatingInterceptor" />
Finally, the server side XWSS configuration is read from a file named wss-server-config.xml from the classpath. With this configuration, XWSS will expect a WS-Security header with a user name token
<xwss:SecurityConfiguration xmlns:xwss="">
    <xwss:RequireUsernameToken passwordDigestRequired="false" nonceRequired="false"/>

Client Side

Adding WS-Security support to the client side is quite simple. Spring-WS client provides WebServiceMessageCallback in order to operate on the message before it is sent. We will use it to replace the message with a secured one. But first we have to configure XWSS on the client side. In the XML configuration file, we will instruct XWSS to include a user name token in the SOAP header.

<xwss:SecurityConfiguration dumpMessages="true" xmlns:xwss="">
    <xwss:UsernameToken name="cafe" password="babe" useNonce="false" digestPassword="false"/>
The dumpMessages=true will cause the secured message to be printed in the console which is useful for debugging. A full description of the available configuration options can be found here.
In we need to use a com.sun.xml.wss.XWSSProcessor and a com.sun.xml.wss.ProcessingContext to process the message.

public class EchoClient extends WebServiceGatewaySupport {
  private static XWSSProcessor cprocessor;
  private static ProcessingContext context;
  public static void main(String[] args) throws IOException,
      XWSSecurityException {
    Resource xwssConfig = new ClassPathResource("wss-client-config.xml");
    if (!xwssConfig.exists()) {
      throw new FileNotFoundException();

    XWSSProcessorFactory factory = XWSSProcessorFactory.newInstance();
    cprocessor = factory.createProcessorForSecurityConfiguration(xwssConfig
                             .getInputStream(), null);
    context = new ProcessingContext();
The interesting part is in the echo() method. We will use the sendAndReceive that accepts a WebServiceMessageCallback. Please note that XWSS only works with SAAJ so SaajSoapMessageFactory have to be used.

  new WebServiceMessageCallback() {
    public void doInMessage(WebServiceMessage message)
      throws IOException {
      // We can cast safley to SaajSoapMessage
      SaajSoapMessage saajSoapMessage = (SaajSoapMessage) message;
      SOAPMessage saajMessage = saajSoapMessage.getSaajMessage();
      try {
        SOAPMessage securedMessage = cprocessor
      catch (XWSSecurityException e) {
        throw new XwsSecuritySecurementException(e.getMessage());
}, result);
If every thing works ok, you should see this on your console (name spaces omitted for brevity):
INFO: ==== Sending Message Start ====
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="">
<wsse:Security xmlns:wsse="..." SOAP-ENV:mustUnderstand="1">
<wsse:UsernameToken xmlns:wsu="..." wsu:Id="XWSSGID-1172860158358-935964365">
<wsse:Password Type="...#PasswordText">****</wsse:Password>
<echoRequest xmlns="...">Hello</echoRequest>
==== Sending Message End  ====