After quite a bit of google-digging and a lot of trial and error, I was able to get past this issue.
First, you'll need to inject a custom handlerResolver into your JaxWsPortProxyFactoryBean.
You'll also need to create a custom HandlerResolver(javax.xml.ws.handler.HandlerResolv er).
So your application context will have something like this in it:
Code:
<bean id="handlerResolver" class="com.example.MySecurityHandlerResolver"/>
<bean id="sampleService" class="org.springframework.remoting.jaxws.JaxWsPortProxyFactoryBean"
p:serviceInterface="com.example.SampleSoap"
p:wsdlDocumentUrl="http://example.com/sample?wsdl"
p:namespaceUri="http://example.com/sample"
p:serviceName="SampleService"
p:portName="SampleSoap"
p:endpointAddress="http://example.com/myendpoint"
>
<property name="handlerResolver" ref="handlerResolver"/>
</bean>
Then, its time to create your HandlerResolver implementation.
First, a basic implementation where you manually build your header. I'm assuming my desired xml will look something like
Code:
<Security>
<UsernameToken>
<Username>bob</Username>
<Password>bobspwd</Password>
</UsernameToken>
</Security>
So, the brute force method:
Code:
public class MySecurityHandlerResolver implements HandlerResolver {
private final static Logger log = LoggerFactory.getLogger(MySecurityHandlerResolver.class);
@Override
public List<Handler> getHandlerChain(PortInfo portInfo) {
List<Handler> handlerList = new ArrayList<Handler>();
handlerList.add(new SecurityHandler());
return handlerList;
}
class SecurityHandler implements SOAPHandler<SOAPMessageContext> {
@Override
public boolean handleMessage(SOAPMessageContext context) {
Boolean outboundProperty = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (outboundProperty.booleanValue()) {
SOAPMessage message = context.getMessage();
try {
SOAPPart sp = message.getSOAPPart();
SOAPEnvelope envelope = sp.getEnvelope();
SOAPHeader header = envelope.getHeader();
if (header==null) {
log.info("No header found");
header = envelope.addHeader();
}
final String uri="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";
SOAPFactory factory = SOAPFactory.newInstance();
SOAPElement securityElem = factory.createElement("Security","xx", uri);
SOAPElement userElem = factory.createElement("UsernameToken","xx",uri);
SOAPElement username = factory.createElement("Username","xx",uri);
username.setTextContent("Bob");
SOAPElement password = factory.createElement("Password","xx",uri);
password.setTextContent("bobspwd");
userElem.addChildElement(username);
userElem.addChildElement(password);
securityElem.addChildElement(userElem);
} catch (Exception e) {
log.error("Exception in handler: " + e);
return false;
}
} else {
// inbound
}
return true;
}
@Override
public boolean handleFault(SOAPMessageContext context) {
return true;
}
@Override
public void close(MessageContext context) {
// no-op
}
@Override
public Set<QName> getHeaders() {
return new TreeSet<QName>();
}
}
}
Now, if you're like me, you have a package of generated code for org.oasis_open.docs.wss._2004._01.oasis_200401_wss _wssecurity_secext_1_0 that you really want to use.
For this, you'll want to marshal your security section into your header....
Code:
public class MySecurityHandlerResolver implements HandlerResolver {
private final static Logger log = LoggerFactory.getLogger(MySecurityHandlerResolver.class);
private static JAXBContext JAXB_CONTEXT;
static {
try {
JAXB_CONTEXT = JAXBContext.newInstance(SecuritySoapHeader.class);
} catch (JAXBException e) {
log.error("Unable to create the JAXB context", e);
}
}
@Override
public List<Handler> getHandlerChain(PortInfo portInfo) {
List<Handler> handlerList = new ArrayList<Handler>();
handlerList.add(new SecurityHandler());
return handlerList;
}
class SecurityHandler implements SOAPHandler<SOAPMessageContext> {
@Override
public boolean handleMessage(SOAPMessageContext context) {
Boolean outboundProperty = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (outboundProperty.booleanValue()) {
final ObjectFactory of = new ObjectFactory();
SOAPMessage message = context.getMessage();
try {
SOAPPart sp = message.getSOAPPart();
SOAPEnvelope envelope = sp.getEnvelope();
SOAPHeader header = envelope.getHeader();
if (header==null) {
log.info("No header found");
header = envelope.addHeader();
}
Marshaller marshaller = JAXB_CONTEXT.createMarshaller();
// Add the UserNameToken.
SecuritySoapHeader secHead = of.createSecuritySoapHeader();
UsernameToken token = of.createUsernameToken();
token.setUsername("Bob");
token.setPassword(bobspwd");
secHead.setUsernameToken(token);
JAXBElement<SecuritySoapHeader> sec = of.createSecurity(secHead);
marshaller.marshal(sec, header);
} catch (Exception e) {
log.error("Exception in handler: " + e);
return false;
}
} else {
// inbound
}
return true;
}
@Override
public boolean handleFault(SOAPMessageContext context) {
return true;
}
@Override
public void close(MessageContext context) {
// no-op
}
@Override
public Set<QName> getHeaders() {
return new TreeSet<QName>();
}
}
}
There you go, that should do it.