I am having troubles porting an old application to Spring Boot 3.
The old app is a Spring Boot 2 microservice using Axis, and I am in the progress of migrating such code to Spring WS according to refactoring/modernization requirements.
This application has a number of SOAP clients connecting to external services, authenticated using WS-Security username/password.
Example header (from a working SOAPUI request):
<soapenv:Header>
<wsse:Security xmlns:wsse="
http://schemas.xmlsoap.org/ws/2003/06/secext"
xmlns:wsu="
http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<wsse:UsernameToken wsu:Id="UsernameToken-2">
<wsse:Username>XXX</wsse:Username>
<wsse:Password>YYY</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
</soapenv:Header>
Old code generated its own SOAP header with custom code. I found that Spring-WS supports WSS4J interceptor, so I used this code
/**
* Factory method do create the client with common settings
* <ul>
* <li>Jaxb2 marshaller</li>
* <li>Authentication interceptor</li>
* </ul>
*
* @param jaxb2RootClass Root class of the Jaxb2 schema
* @param settings URL and authentication to the web service
* @return An instance of the web service client class
*/
protected WebServiceTemplate factorWebServiceBean(@NonNull Class<?> jaxb2RootClass, @NonNull ServiceProperties settings) {
var ret = new WebServiceTemplate();
ret.setDefaultUri(settings.getUrl());
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setPackagesToScan(jaxb2RootClass.getPackageName());
marshaller.setCheckForXmlRootElement(false);
marshaller.setBeanClassLoader(beanClassLoader);
ret.setMarshaller(marshaller);
ret.setUnmarshaller(marshaller);
if (isNoneBlank(settings.getUsername(), settings.getPassword())) {
var interceptor = new Wss4jSecurityInterceptor();
interceptor.setSecurementActions(WSHandlerConstants.USERNAME_TOKEN);
interceptor.setSecurementPasswordType(WSConstants.PW_TEXT);
interceptor.setSecurementUsername(settings.getUsername());
interceptor.setSecurementPassword(settings.getPassword());
ret.setInterceptors(new ClientInterceptor[]{interceptor});
}
return ret;
}
The result is not working as expected.
Following is an example SOAP request:
<SOAP-ENV:Header>
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
SOAP-ENV:mustUnderstand="1">
<wsse:UsernameToken wsu:Id="UsernameToken-38690769-f733-473c-8dd3-8eed438678c2">
<wsse:Username>XXX</wsse:Username>
<wsse:Password
Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">
YYYY
</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
</SOAP-ENV:Header>
And the according response from the remote endpoint
The Application Server expected a Security header with the
http://schemas.xmlsoap.org/ws/2003/06/secext
or
http://schemas.xmlsoap.org/ws/2002/07/secext
or
http://schemas.xmlsoap.org/ws/2002/04/secext
namespace, but it was not found
What is wrong with my code? I can obviously see the namespace is different. Is it possible to specify Wss4j to use a different namespace instead of reimplementing the wheel security header?