Search code examples
javasoapsoapuispring-ws

Spring-WS SOAP header prefix issues


I'm working on a Spring-WS project and I'm trying to consume a certain SOAP service but I'm getting some issues with the request's Header tag.
PS: I ran the same request on SOAP UI and it works perfectly.

This is the code I ran:

JAXBElement<ChangeOtherIDsRequestType> request = createRequestBody();

    WebServiceTemplate template = new WebServiceTemplate(marshaller);
    template.setDefaultUri(URI);
    @SuppressWarnings({ "unchecked" })
    JAXBElement<ChangeOtherIDsResponseType> response = (JAXBElement<ChangeOtherIDsResponseType>) template
            .marshalSendAndReceive(request, new WebServiceMessageCallback() {

                @Override
                public void doWithMessage(WebServiceMessage message) {
                    Instant instant = Instant.now();

                    SaajSoapMessage saajSoapMessage = (SaajSoapMessage) message;
                    SOAPMessage soapMessage = saajSoapMessage.getSaajMessage();
                    SOAPPart soapPart = soapMessage.getSOAPPart();
                    try {
                        SOAPEnvelope soapEnvelope = soapPart.getEnvelope();
                        SOAPHeader soapHeader = soapEnvelope.getHeader();
                        Name headerElementName = soapEnvelope.createName("Security", "wsse",
                                "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");

                        // Add "Security" soapHeaderElement to soapHeader
                        SOAPHeaderElement soapHeaderElement = soapHeader.addHeaderElement(headerElementName);
                        soapHeaderElement.setMustUnderstand(true);
                        soapHeaderElement.addNamespaceDeclaration("wsu",
                                "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd");

                        // Add usernameToken to "Security" soapHeaderElement
                        SOAPElement usernameTokenSOAPElement = soapHeaderElement.addChildElement("UsernameToken",
                                "wsse");

                        // Add username to usernameToken
                        QName userQname = soapHeaderElement.createQName("Username", "wsse");
                        SOAPElement userNameSOAPElement = usernameTokenSOAPElement.addChildElement(userQname);
                        userNameSOAPElement.addTextNode("username");
                        // Add password to usernameToken
                        QName passwordQname = usernameTokenSOAPElement.createQName("Password", "wsse");
                        SOAPElement passwordSOAPElement = usernameTokenSOAPElement.addChildElement("Password");
                        passwordSOAPElement.setAttribute("Type",
                                "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText");
                        passwordSOAPElement.addTextNode("password");
                        // Add Nonce to usernameToken.
                        QName nonceQname = soapHeaderElement.createQName("Nonce", "wsse");
                        SOAPElement nonceSOAPElement = usernameTokenSOAPElement.addChildElement("Nonce");
                        nonceSOAPElement.setAttribute("EncodingType",
                                "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary");

                        // Add Created to usernameToken.
                        QName createdQname = soapHeaderElement.createQName("Created", "wsu");
                        SOAPElement createdElement = usernameTokenSOAPElement.addChildElement(createdQname);
                        createdElement.addTextNode(instant.toString());
                    } catch (SOAPException | UnsupportedEncodingException e) {
                        e.printStackTrace();
                    }
                }
            });
    template.marshalSendAndReceive(URI, request);  

This code is supposed to add the Header tag in the request and it serves to authenticate the user through "username" and "password".
The result I'm expecting is the same that is generated by SOAP UI for the same request. The result is this:

<SOAP-ENV:Header>
<wsse:Security SOAP-ENV:mustUnderstand="1" 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">
    <wsse:UsernameToken wsu:Id="UsernameToken-C17C5A667A3F11A8DA153735499778322">
        <wsse:Username>username</wsse:Username>
        <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">password</wsse:Password>
        <wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">Tl6jznnHitsSE9F16FWTsw==</wsse:Nonce>
        <wsu:Created>2018-09-19T11:03:17.783Z</wsu:Created>
    </wsse:UsernameToken>
</wsse:Security>

But the result I'm getting looks like this:

<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>
            <wsse:Username>username</wsse:Username>
            <Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">password</Password>
            <Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary"> </Nonce>
            <wsu:Created>2018-09-19T11:02:21.918Z</wsu:Created>
        </wsse:UsernameToken>
    </wsse:Security>
</SOAP-ENV:Header>  

Let's see that there are some things that are missing in my result such as:

  • "wsse" prefix on the tags "Password" and "Nonce".
  • "UsernameToken" attribute on the "Username" tag.
  • The value of "Nonce" tag.

I faced some issues when trying to fix these things but I still have some problems:

  1. When I try to add "wsse" prefix to "Username" and "Nonce" tags: the whole "Header" tag dissapears. (I used the same technique as in the "Username" tag)
  2. How does SOAP UI generate the "Nonce" value ?
  3. How does SOAP UI generate the "UsernameToken" value ?

Last resort, is there another way to mock the SOAP UI request ?

Thanks in advance.


Solution

  • You are trying to shoehorn security into your request whereas there is an interceptor and default way of doing that. Configure the Wss4jSecurityInterceptor for your WebServiceTemplate.

    Something like the following should do the trick.

    @Bean
    public Wss4jSecurityInterceptor securityInterceptor() {
      Wss4jSecurityInterceptor interceptor = new Wss4jSecurityInterceptor();
      interceptor.setSecurementUsername("username");
      interceptor.setSecurementPassword("password");
      interceptor.setSecurementUsernameTokenNonce(true);
      interceptor.setSecurementActions("UsernameToken Timestamp");
      return interceptor;
    }
    

    Then inject this into your WebServiceTemplate. That should add the needed headers without you having to do anything else. Ideally you would configure the WebServiceTemplate once and reuse it.

    @Bean
    public WebServiceTemplate webServiceTemplate(Marshaller marshaller) {
        WebServiceTemplate wsTemplate = new WebServiceTemplate(marshaller);
        wsTemplate.setInterceptors(new ClientInterceptor[]{ securityInterceptor() });
        return wsTemplate;
    }
    

    Then use the pre configured WebServiceTemplate in your code.