为什么我的JAX-WS处理程序在每个元素上重新声明相同的命名空间?

时间:2012-09-19 20:01:12

标签: java xml soap jax-ws

我编写了一个JAX-WS处理程序,用于向我的SOAP客户端的出站消息添加WS-Security标头:

package com.soap.client;

import javax.xml.namespace.QName;
import javax.xml.soap.Name;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPFactory;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;

public class ClientHeaderHandler implements SOAPHandler<SOAPMessageContext> {

    private static final String WSSECURITY_PREFIX = "wsse";
    private static final String WSSECURITY_NAMESPACE = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";
    private static final String PASSWORD_TEXT_TYPE = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText";

    /**
     * {@inheritDoc}
     * @see javax.xml.ws.handler.Handler#handleMessage(javax.xml.ws.handler.MessageContext)
     */
    @Override
    public boolean handleMessage(final SOAPMessageContext context) {
        boolean outbound = false;
        outbound = (Boolean) context.get (MessageContext.MESSAGE_OUTBOUND_PROPERTY);

        if (outbound) {
            try {
                addSecurityHeader(context);             
            } catch (SOAPException e) {
                // do nothing
            }
        }

        return true;
    }

    private void addSecurityHeader(final SOAPMessageContext context) throws SOAPException {
        SOAPFactory sf = SOAPFactory.newInstance();
        SOAPElement securityElem = sf.createElement("Security", WSSECURITY_PREFIX, WSSECURITY_NAMESPACE);
        SOAPElement tokenElem = sf.createElement("UsernameToken", WSSECURITY_PREFIX, WSSECURITY_NAMESPACE);
        SOAPElement usernameElem = sf.createElement("Username", WSSECURITY_PREFIX, WSSECURITY_NAMESPACE);
        usernameElem.addTextNode("myusername");
        tokenElem.addChildElement(usernameElem);

        Name passwordTypeName = sf.createName("Type", WSSECURITY_PREFIX, WSSECURITY_NAMESPACE);
        SOAPElement passwordElem = sf.createElement("Password", WSSECURITY_PREFIX, WSSECURITY_NAMESPACE);
        passwordElem.addAttribute(passwordTypeName, PASSWORD_TEXT_TYPE);
        passwordElem.addTextNode("mypassword");

        tokenElem.addChildElement(passwordElem);
        securityElem.addChildElement(tokenElem);
        context.getMessage().getSOAPPart().getEnvelope().addHeader().addChildElement(securityElem);
    }
}

这主要起作用;但是,WS-Security名称空间和前缀在它们使用的每个元素上重新声明( xmlns:wsse = http:// ... ):

<?xml version='1.0' encoding='UTF-8'?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
    <S:Header>
        <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
            <wsse:UsernameToken xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
                <wsse:Username xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">myusername</wsse:Username>
                <wsse:Password wsse:Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">mypassword</wsse:Password>
            </wsse:UsernameToken>
        </wsse:Security>
    </S:Header>
    <S:Body>
        <MyBody/>
    </S:Body>
</S:Envelope>

我尝试了各种QNames,Names等组合,但我似乎无法做到这一点。 我需要更改哪些内容才能在最顶层的安全元素中声明WS-Security名称空间?


更新: gpeche的建议对我有用。从使用SOAPFactory创建元素切换,然后通过addChildElement将其附加到通过addChildElement直接创建它:

private void addSecurityHeader(final SOAPMessageContext context) throws SOAPException {
    SOAPFactory sf = SOAPFactory.newInstance();
    SOAPElement securityElem = context.getMessage().getSOAPPart().getEnvelope().addHeader().addChildElement("Security", WSSECURITY_PREFIX, WSSECURITY_NAMESPACE);
    SOAPElement tokenElem = securityElem.addChildElement("UsernameToken", WSSECURITY_PREFIX, WSSECURITY_NAMESPACE);
    SOAPElement usernameElem = tokenElem.addChildElement("Username", WSSECURITY_PREFIX, WSSECURITY_NAMESPACE);
    usernameElem.addTextNode("myusername");

    Name passwordTypeName = sf.createName("Type", WSSECURITY_PREFIX, WSSECURITY_NAMESPACE);
    SOAPElement passwordElem = tokenElem.addChildElement("Password", WSSECURITY_PREFIX, WSSECURITY_NAMESPACE);
    passwordElem.addAttribute(passwordTypeName, PASSWORD_TEXT_TYPE);
    passwordElem.addTextNode("mypassword");
}

生成更清晰的XML:

<?xml version='1.0' encoding='UTF-8'?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
    <S:Header>
        <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
            <wsse:UsernameToken>
                <wsse:Username>myusername</wsse:Username>
                <wsse:Password wsse:Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">mypassword</wsse:Password>
            </wsse:UsernameToken>
        </wsse:Security>
    </S:Header>
    <S:Body>
        <MyBody/>
    </S:Body>
</S:Envelope>

1 个答案:

答案 0 :(得分:3)

我遇到了同样的问题,我发现可靠的唯一方法就是按节点重新生成XML树节点:

  1. 引用您要在其中设置命名空间的节点
  2. 将子项附加到其父项,并使用所需的前缀,名称,属性和名称空间。
  3. 递归地将后代复制到新子项中,调整前缀和名称空间
  4. 完成后,分离原始节点
  5. 但是我一直认为必须有一种更简单的方法......

    <强>更新

    好的我想我知道你的问题是什么:你是直接从SOAPElement创建所有SOAPFactory的。在创建时SOAPElement没有父集,因此它们不能继承您从任何人指定的名称空间,并决定自己编写名称空间声明。一旦创建了它们,如果他们可以放弃任何声明,他们就不会在append() / setParent()时间检查。

    您是否可以尝试仅创建SOAPFactory中最外层的元素,然后通过SOAPElement.addChildElement()创建其余元素?