簡體   English   中英

JAX-WS - 添加 SOAP 標頭

[英]JAX-WS - Adding SOAP Headers

我正在嘗試創建一個獨立的客戶端來使用一些 Web 服務。 我必須將我的用戶名和密碼添加到 SOAP 標頭中。 我嘗試添加憑據如下:

OTSWebSvcsService service = new OTSWebSvcsService();
OTSWebSvcs port = service.getOTSWebSvcs();

BindingProvider prov = (BindingProvider)port;
prov.getRequestContext().put(BindingProvider.USERNAME_PROPERTY, "myusername");
prov.getRequestContext().put(BindingProvider.PASSWORD_PROPERTY, "mypassword");

...

當我在服務上調用方法時,出現以下異常:

com.ibm.wsspi.wssecurity.SoapSecurityException: WSEC5048E: One of "SOAP Header" elements required.

我究竟做錯了什么? 我如何將這些屬性添加到 SOAP 標頭?

編輯:我使用的是 JDK6 中包含的 JAX-WS 2.1。 我現在使用 JAX-WS 2.2。 我現在得到以下異常:

com.ibm.wsspi.wssecurity.SoapSecurityException: WSEC5509E: A security token whose type is [http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#UsernameToken] is required.

我該如何創建這個令牌?

可以使用@WebParam(header = true) 在 SOAP 標頭 (JaxWS) 中傳輸數據:

@WebMethod(operationName = "SendRequest", action = "http://abcd.ru/")
@Oneway
public void sendRequest(
    @WebParam(name = "Message", targetNamespace = "http://abcd.ru/", partName = "Message")
    Data message,
    @WebParam(name = "ServiceHeader", targetNamespace = "http://abcd.ru/", header = true, partName = "ServiceHeader")
    Header serviceHeader);

如果要生成帶有 SOAP Header 的客戶端,則需要使用 -XadditionalHeaders:

wsimport -keep -Xnocompile -XadditionalHeaders -Xdebug http://12.34.56.78:8080/TestHeaders/somewsdl?wsdl -d /home/evgeny/DEVELOPMENT/JAVA/gen

如果不需要@Oneway web service,可以使用Holder:

@WebMethod(operationName = "SendRequest", action = "http://abcd.ru/")
public void sendRequest(
    @WebParam(name = "Message", targetNamespace = "http://abcd.ru/", partName = "Message")
    Data message,
    @WebParam(name = "ServiceHeader", targetNamespace = "http://abcd.ru/", header = true, partName = "ServiceHeader")
    Holder<Header> serviceHeader);

不是 100% 確定,因為問題缺少一些細節,但如果您使用的是 JAX-WS RI,請查看發送請求時添加 SOAP 標頭

這樣做的可移植方法是創建一個SOAPHandler並使用 SAAJ,但 RI 提供了一種更好的方法。

當您創建代理或調度對象時,它們會實現BindingProvider接口。 當您使用 JAX-WS RI 時,您可以向下轉換到WSBindingProvider ,它定義了一些僅由 JAX-WS RI 提供的方法。

此接口允許您設置任意數量的 Header 對象,每個對象代表一個 SOAP 標頭。 如果需要,您可以自己實現它,但很可能您會使用在Headers類中定義的工廠方法之一來創建一個。

 import com.sun.xml.ws.developer.WSBindingProvider; HelloPort port = helloService.getHelloPort(); // or something like that... WSBindingProvider bp = (WSBindingProvider)port; bp.setOutboundHeader( // simple string value as a header, like <simpleHeader>stringValue</simpleHeader> Headers.create(new QName("simpleHeader"),"stringValue"), // create a header from JAXB object Headers.create(jaxbContext,myJaxbObject) );

相應地更新您的代碼並重試。 如果您沒有使用 JAX-WS RI,請更新您的問題並提供更多上下文信息。

更新:您要調用的 Web 服務似乎使用 WS-Security/UsernameTokens 進行保護。 這與您最初的問題有點不同。 無論如何,要將您的客戶端配置為發送用戶名和密碼,我建議查看為基於 Metro 的 Web 服務實現 WS-Security UsernameToken Profile的精彩文章(跳轉到第 4 步)。 在這一步使用 NetBeans 可能會讓事情變得輕松很多。

此外,如果您使用 Maven 構建項目,則需要添加以下依賴項:

    <dependency>
        <groupId>com.sun.xml.ws</groupId>
        <artifactId>jaxws-rt</artifactId>
        <version>{currentversion}/version>
    </dependency>

這為您提供了類com.sun.xml.ws.developer.WSBindingProvider

鏈接: https : //mvnrepository.com/artifact/com.sun.xml.ws/jaxws-rt

我添加這個答案是因為其他人都沒有為我工作。

我不得不向代理添加一個Header Handler

import java.util.Set;
import java.util.TreeSet;

import javax.xml.namespace.QName;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPFactory;
import javax.xml.soap.SOAPHeader;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;

public class SOAPHeaderHandler implements SOAPHandler<SOAPMessageContext> {

    private final String authenticatedToken;

    public SOAPHeaderHandler(String authenticatedToken) {
        this.authenticatedToken = authenticatedToken;
    }

    public boolean handleMessage(SOAPMessageContext context) {
        Boolean outboundProperty =
                (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
        if (outboundProperty.booleanValue()) {
            try {
                SOAPEnvelope envelope = context.getMessage().getSOAPPart().getEnvelope();
                SOAPFactory factory = SOAPFactory.newInstance();
                String prefix = "urn";
                String uri = "urn:xxxx";
                SOAPElement securityElem =
                        factory.createElement("Element", prefix, uri);
                SOAPElement tokenElem =
                        factory.createElement("Element2", prefix, uri);
                tokenElem.addTextNode(authenticatedToken);
                securityElem.addChildElement(tokenElem);
                SOAPHeader header = envelope.addHeader();
                header.addChildElement(securityElem);
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            // inbound
        }
        return true;
    }

    public Set<QName> getHeaders() {
        return new TreeSet();
    }

    public boolean handleFault(SOAPMessageContext context) {
        return false;
    }

    public void close(MessageContext context) {
        //
    }
}

在代理中,我只添加了處理程序:

BindingProvider bp =(BindingProvider)basicHttpBindingAuthentication;
bp.getBinding().getHandlerChain().add(new SOAPHeaderHandler(authenticatedToken));
bp.getBinding().getHandlerChain().add(new SOAPLoggingHandler());

您可以將用戶名和密碼添加到 SOAP Header

BindingProvider prov = (BindingProvider)port;
prov.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY,
                "your end point"));
Map<String, List<String>> headers = new HashMap<String, List<String>>();
prov.getRequestContext().put(BindingProvider.USERNAME_PROPERTY, "myusername");
prov.getRequestContext().put(BindingProvider.PASSWORD_PROPERTY, "mypassword");
prov.getRequestContext().put(MessageContext.HTTP_REQUEST_HEADERS, headers);

使用 maven 和插件jaxws-maven-plugin 這將生成一個 Web 服務客戶端。 確保將xadditionalHeaders設置為 true。 這將生成帶有標題輸入的方法。

最好的選擇(當然對我來說)是自己動手。 這意味着您可以以編程方式修改 SOAP 消息的所有部分

Binding binding = prov.getBinding();
   List<Handler> handlerChain = binding.getHandlerChain();
    handlerChain.add( new ModifyMessageHandler() );
    binding.setHandlerChain( handlerChain ); 

而 ModifyMessageHandler 源可能是

@Override
public boolean handleMessage( SOAPMessageContext context )
{
    SOAPMessage msg = context.getMessage(); 
    try
    {

        SOAPEnvelope envelope = msg.getSOAPPart().getEnvelope();
        SOAPHeader header = envelope.addHeader();
        SOAPElement ele = header.addChildElement( new QName( "http://uri", "name_of_header" ) );
        ele.addTextNode( "value_of_header" );
        ele = header.addChildElement( new QName( "http://uri", "name_of_header" ) );
        ele.addTextNode( "value_of_header" );
        ele = header.addChildElement( new QName( "http://uri", "name_of_header" ) );
        ele.addTextNode( "value_of_header" );

...

我希望這可以幫助你

jaxws-rt-2.2.10-ources.jar!\\com\\sun\\xml\\ws\\transport\\http\\client\\HttpTransportPipe.java

public Packet process(Packet request) {
        Map<String, List<String>> userHeaders = (Map<String, List<String>>) request.invocationProperties.get(MessageContext.HTTP_REQUEST_HEADERS);
        if (userHeaders != null) {
            reqHeaders.putAll(userHeaders);

因此,帶有鍵MessageContext.HTTP_REQUEST_HEADERS requestContext 中的Map<String, List<String>>將被復制到 SOAP 標頭。 通過標頭使用 JAX-WS 進行應用程序身份驗證的示例

BindingProvider.USERNAME_PROPERTYBindingProvider.PASSWORD_PROPERTY鍵在HttpTransportPipe.addBasicAuth()以特殊方式處理,添加了標准的基本授權Authorization標頭。

另請參閱JAX-WS 中的消息上下文

我在這里掙扎了所有答案,從Pascal 的解決方案開始,隨着 Java 編譯器不再默認綁定rt.jar (並且使用內部類使其特定於該運行時實現),這變得越來越難。

edubriguenti 的回答讓我很接近。 但是,處理程序在最后一段代碼中的連接方式對我不起作用 - 它從未被調用過。

我最終使用了他的處理程序類的變體,但將其連接到javax.xml.ws.Service實例中,如下所示:

Service service = Service.create(url, qname); service.setHandlerResolver( portInfo -> Collections.singletonList(new SOAPHeaderHandler(handlerArgs)) );

將對象添加到標題我們使用此處使用的示例,但我將完成

  ObjectFactory objectFactory = new ObjectFactory();
        CabeceraCR cabeceraCR =objectFactory.createCabeceraCR();
        cabeceraCR.setUsuario("xxxxx");
        cabeceraCR.setClave("xxxxx");

使用對象工廠,我們創建了要求傳遞標頭的對象。 添加到標題的

  WSBindingProvider bp = (WSBindingProvider)wsXXXXXXSoap;
        bp.setOutboundHeaders(
                // Sets a simple string value as a header
                Headers.create(jaxbContext,objectFactory.createCabeceraCR(cabeceraCR))
                );

我們使用 WSBindingProvider 添加標頭。 直接使用該對象會有一些錯誤,所以我們使用該方法

objectFactory.createCabeceraCR(cabeceraCR)

此方法將在對象工廠上創建一個像這樣的 JAXBElement

  @XmlElementDecl(namespace = "http://www.creditreport.ec/", name = "CabeceraCR")
    public JAXBElement<CabeceraCR> createCabeceraCR(CabeceraCR value) {
        return new JAXBElement<CabeceraCR>(_CabeceraCR_QNAME, CabeceraCR.class, null, value);
    }

我們獲得的 jaxbContext 是這樣的:

  jaxbContext = (JAXBRIContext) JAXBContext.newInstance(CabeceraCR.class.getPackage().getName());

這會將對象添加到標題中。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM