I wrote a JAX-WS handler to add the WS-Security header to my outgoing SOAP client messages:
package com.soap.client; import javax.xml.namespace.QName; import javax.xml.soap.Name; import javax.xml.soap.SOAPElement; import javax.xml.soap.SOAPException; import javax.xml.soap.SOAPFactory; import javax.xml.ws.handler.MessageContext; import javax.xml.ws.handler.soap.SOAPHandler; import javax.xml.ws.handler.soap.SOAPMessageContext; public class ClientHeaderHandler implements SOAPHandler<SOAPMessageContext> { private static final String WSSECURITY_PREFIX = "wsse"; private static final String WSSECURITY_NAMESPACE = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"; private static final String PASSWORD_TEXT_TYPE = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText"; @Override public boolean handleMessage(final SOAPMessageContext context) { boolean outbound = false; outbound = (Boolean) context.get (MessageContext.MESSAGE_OUTBOUND_PROPERTY); if (outbound) { try { addSecurityHeader(context); } catch (SOAPException e) {
It basically works; however, the namespace and WS-Security prefix are re-declared for each element in which they are used ( xmlns: wsse = http: // ... ):
<?xml version='1.0' encoding='UTF-8'?> <S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"> <S:Header> <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> <wsse:UsernameToken xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> <wsse:Username xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">myusername</wsse:Username> <wsse:Password wsse:Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">mypassword</wsse:Password> </wsse:UsernameToken> </wsse:Security> </S:Header> <S:Body> <MyBody/> </S:Body> </S:Envelope>
I tried various combinations of QNames, Names, etc., but I can not do this work. What do I need to change so that the WS-Security namespace is declared only in the topmost security element?
UPDATE: The gpeche suggestion below worked for me. Moving from creating an element using SOAPFactory, adding it through addChildElement to create it through addChildElement:
private void addSecurityHeader(final SOAPMessageContext context) throws SOAPException { SOAPFactory sf = SOAPFactory.newInstance(); SOAPElement securityElem = context.getMessage().getSOAPPart().getEnvelope().addHeader().addChildElement("Security", WSSECURITY_PREFIX, WSSECURITY_NAMESPACE); SOAPElement tokenElem = securityElem.addChildElement("UsernameToken", WSSECURITY_PREFIX, WSSECURITY_NAMESPACE); SOAPElement usernameElem = tokenElem.addChildElement("Username", WSSECURITY_PREFIX, WSSECURITY_NAMESPACE); usernameElem.addTextNode("myusername"); Name passwordTypeName = sf.createName("Type", WSSECURITY_PREFIX, WSSECURITY_NAMESPACE); SOAPElement passwordElem = tokenElem.addChildElement("Password", WSSECURITY_PREFIX, WSSECURITY_NAMESPACE); passwordElem.addAttribute(passwordTypeName, PASSWORD_TEXT_TYPE); passwordElem.addTextNode("mypassword"); }
generates much cleaner XML:
<?xml version='1.0' encoding='UTF-8'?> <S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"> <S:Header> <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> <wsse:UsernameToken> <wsse:Username>myusername</wsse:Username> <wsse:Password wsse:Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">mypassword</wsse:Password> </wsse:UsernameToken> </wsse:Security> </S:Header> <S:Body> <MyBody/> </S:Body> </S:Envelope>