How to properly format a SOAP message envelope using a custom SOAPHandler

java, soaphandler, xml

Solution

This is taken from my working handler. Hope it works for you.

public static final String WSSE_NS = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";
public static final String PASSWORD_TEXT_TYPE = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText";
public static final String WSSE_SECURITY_LNAME = "Security";
public static final String WSSE_NS_PREFIX = "wsse";

private String username;
private String password;
private boolean mustUnderstand = false;

public boolean handleMessage(SOAPMessageContext messageContext) {
    Object bOutbound = messageContext.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
    if (bOutbound == Boolean.TRUE) {
        try {
            if (username != null && username.length() != 0) {
                addSecurityHeader(messageContext);
                LOG.debug("Added security header");
            } else {
                LOG.debug("No username configured thus not adding a security header");
            }
        } catch (Exception e) {
            LOG.error("Exception in handleMessage", e);
            return false;
        }
    }
    return true;
}

private void addSecurityHeader(SOAPMessageContext messageContext) throws SOAPException {
    SOAPFactory sf = SOAPFactory.newInstance();
    SOAPHeader header = messageContext.getMessage().getSOAPPart().getEnvelope().getHeader();
    if (header == null) {
        header = messageContext.getMessage().getSOAPPart().getEnvelope().addHeader();
    }

    Name securityName = sf.createName(WSSE_SECURITY_LNAME, WSSE_NS_PREFIX, WSSE_NS);
    SOAPHeaderElement securityElem = header.addHeaderElement(securityName);
    securityElem.setMustUnderstand(mustUnderstand);

    Name usernameTokenName = sf.createName("UsernameToken", WSSE_NS_PREFIX, WSSE_NS);
    SOAPElement usernameTokenMsgElem = sf.createElement(usernameTokenName);

    Name usernameName = sf.createName("Username", WSSE_NS_PREFIX, WSSE_NS);
    SOAPElement usernameMsgElem = sf.createElement(usernameName);
    usernameMsgElem.addTextNode(username);
    usernameTokenMsgElem.addChildElement(usernameMsgElem);

    Name passwordName = sf.createName("Type", WSSE_NS_PREFIX, WSSE_NS);
    SOAPElement passwordMsgElem = sf.createElement("Password", WSSE_NS_PREFIX, WSSE_NS);

    passwordMsgElem.addAttribute(passwordName, PASSWORD_TEXT_TYPE);
    passwordMsgElem.addTextNode(password);
    usernameTokenMsgElem.addChildElement(passwordMsgElem);

    securityElem.addChildElement(usernameTokenMsgElem);
}

Problem

I have a class that implements the SOAPHandler interface. The handleMessage is defined as: ``` public boolean handleMessage(SOAPMessageContext context) { SOAPMessage msg = context.getMessage(); SOAPPart part = msg.getSOAPPart(); SOAPEnvelope envelope = part.getEnvelope(); // add namespaces SOAPElement envelope.addNamespaceDeclaration("xsd", "http://www.w3.org/2001/XMLSchema"); envelope.addNamespaceDeclaration("xsi", "http://www.w3.org/2001/XMLSchema- // add the header with additional elements Name qname = envelope.createName("Security", "sse", "http://example.com/security.xsd"); element = envelope.addHeader().addChildElement(qname); qname = envelope.createName("mustUnderstand"); element.addAttribute(qname, "1"); qname = envelope.createName("UsernameToken", "sse", "http://example.com/user.xsd"); element = envelope.getHeader().addHeaderElement(qname); element.addTextNode("user1"); qname = envelope.createName("Password"); element = envelope.getHeader().addHeaderElement(qname); element.addTextNode("1234"); } } catch (Exception e) { e.printStackTrace(); } return true; } ``` This generates the following message: ``` <S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <S:Header> <sse:Security xmlns:sse="http://example.com/security.xsd" mustUnderstand="1"/> <sse:UsernameToken xmlns:sse="http://example.com/user.xsd">user1</sse:UsernameToken> </S:Header> <S:Body> ....The rest of the transaction </S:Body> </S:Envelope> ``` The problem is I need to generate a message with the following format: ``` <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <soapenv:Header> <sse:Security soapenv:mustUnderstand="1" xmlns:sse="http://example.com/security.xsd"> <sse:UsernameToken wsu:Id="UsernameToken-9993341" xmlns:wsu="http://example.com/user.xsd"> <sse:Username>user1</sse:Username> <sse:Password Type="http://example.com/password#PasswordText">1234</sse:Password> </sse:UsernameToken> </sse:Security> </soapenv:Header> <soapenv:Body> ....The rest of the transaction </soapenv:Body> </soapenv:Envelope> ``` The "mustUnderstand" attribute doesn't have the soapenv prefix, the sse:Security tag is closed right away instead of having the other tags as children, and the UserName isn't properly formatted as ``` <sse:Username>user1</sse:Username> ``` . How can I format the message properly using the SOAPElement methods? The biggest thing I need to know is how to properly next the tags inside of the Security tag and how to have the username/password tags properly formatted. I've tried different combinations of the addHeaderElement and addChildElement methods, but I can't get it formatted properly and the javadocs don't give enough detail about what they will generate.

Original source