theriom.com
This blog is mainly for me, a way of remembering things I've done; when I couldn't find an answer on Google, I wrote about it here. Hopefully, other people may find it helpful too.
Spring and WS-Security Example

I recently had to put in place a public facing soap web service with Spring and WS-Security. While the tutorials on Spring Source are great, if you requirements deviate from the sample tutorials it is difficult to track down any extra information needed.

First off, my requirements were:

  • WS-Security with PKI.
  • Must use the Wss4jSecurityInterceptor interceptor.
  • The body of the soap message was to be signed and encrypted, and some parts of the header must also be signed.
  • Timestamps must be included in the soap header.
  • The body was encrypted so WS-Addressing was required, and I used the ws-addressing action to decide the message/function to call.

There were many clients of the service, each with their own keys, so I needed to use the ws-addressing From address to choose the encryption key for the return message.

A full project isn’t included here, just excerpts from the code where it deviated from the tutorial.

I needed to use Action Endpoint mapping instead of payload mapping.

@Action(value=REQUEST_ACTION, output=RESPONSE_ACTION)
@ResponsePayload
public Element handleRequest(@RequestPayload Element)
{

REQUEST_ACTION and RESPONSE_ACTION match the request and response action from your WSDL.

The next problem I hit was the interceptor chain, shown below, didn’t appear to get invoked when the action endpoint mapping when configured in spring-ws-servlet.xml.

So, the interceptor chain had to be configured in code, instead of xml.

Notice that I used setSecurementParts() to specify the parts of the message I wanted signed.

Next, the response needs to be encrypted using the public key of the requester. In the tutorials this is done by Wss4jSecurityInterceptor.securementEncryptionUser(), but this sets the encryption user globally for the interceptor, so all requests will be encrypted with the key of the user set here, and since I wouldn’t know the requester until run time, I needed to call this for every request.

To do this, I extended Wss4jSecurityInterceptor and overrode the secureMessage() method, this method is called everytime the message needs to be secured.

It’s important to make sure that the scope of Wss4jSecurityInterceptorWithNClients is set to Request, because this method is not thread safe.

Next, I changed ActionInterceptorImpl to use Wss4jSecurityInterceptorWithNClients instead of Wss4jSecurityInterceptor.

There were two other issues I noticed: The Interceptor will verify all signatures in the request, but I couldn’t find a way for it to ensure/enforce particular fields were signed. In the end I checked that the fields which required signatures has them by examining their attributes. If the WS-A ReplyTo field was set to something invalid in the request, it caused an exception which broke the interceptor chain and resulted in unsecured responses being set. As a quick work around I removed any ReplyTo entry as I didn’t require it.


Last modified on 2012-06-23