Problem applying wsse security (username/password) to Spring Boot WS WebServiceTemplate

33 Views Asked by At

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?

0

There are 0 best solutions below