4

SOAP クライアントの送信メッセージに WS-Security ヘッダーを追加する JAX-WS ハンドラーを作成しました。

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 名前空間が最上位の 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>
4

1 に答える 1

3

私は同じ問題を抱えていましたが、それを確実に行う唯一の方法は、ノードごとに XML ツリーを再生成することでした。

  1. 名前空間を設定するノードへの参照を取得します
  2. 必要なプレフィックス、名前、属性、および名前空間を使用して、子を親に追加します。
  3. 子孫を再帰的に新しい子にコピーし、プレフィックスと名前空間を適応させます
  4. 完了したら、元のノードを切り離します

しかし、私はもっと簡単な方法があるに違いないと考え続けています...

アップデート:

わかりました、あなたの問題が何であるかがわかると思いますSOAPElement.. SOAPFactory作成時SOAPElementには s には親セットがないため、指定した名前空間を誰からも継承できず、名前空間宣言自体を記述することにします。それらが作成されると、宣言を削除できるかどうかをいつでもチェックする必要はありませappend()setParent()

から最も外側の要素だけSOAPFactoryを作成し、残りを 経由で作成してみてくださいSOAPElement.addChildElement()

于 2012-09-19T20:11:37.327 に答える