如何使用JAX-WS向SOAP请求添加SOAP标头?

7
我们需要使用其他团队开发的Web服务。使用JAX-WS生成Web服务。我们使用wsimport生成客户端存根。
问题是我需要将以下信息作为标题与SOAP正文一起传递:
<soapenv:Header>
    <ns1:HeaderData xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" 
                    xmlns:ns1="http://www.example.com/esb/data_type/HeaderData/v1">
        <ChannelIdentifier>ABC</ChannelIdentifier>
    </ns1:HeaderData>
</soapenv:Header>

我们正在使用:
BindingProvider.getRequestContext().put(
    BindingProvider.ENDPOINT_ADDRESS_PROPERTY,
    serviceConfig.getServiceEndPoint()
);

设置终端点。

有人能建议如何在请求中传递头信息吗?

谢谢, VK

2个回答

13

使用@WebParam注释的header = true参数

@WebMethod
@WebResult String method(
      @WebParam String anotherParam
      @WebParam(header = true, mode = Mode.OUT) Holder<String> headerParam)  

header = true, mode = Mode.OUT 的意思是参数 headerParam 只会在输出的时候出现在头部。
如果您想要这个参数既在输入中又在输出中,请使用 Mode.INOUT


7

我需要将用户凭证添加到标头中,因此我这样做:

private void modifyRequest(String username, String password) {
    UserCredentials authHeader = new UserCredentials();
    authHeader.setUsername(username);
    authHeader.setPassword(password);
    ArrayList<Header> headers = new ArrayList<Header>(1);
    try {
        Header soapHeader = new Header(new QName(TQIntegrationV2.TQIntegrationV2Soap.getNamespaceURI(), "UserCredentials"), authHeader, new JAXBDataBinding(UserCredentials.class));
        headers.add(soapHeader);
    } catch (JAXBException ex) {
        LOGGER.error("Exception trying to serialize header: {}", ex);
    }
    ((BindingProvider) proxy).getRequestContext().put(Header.HEADER_LIST, headers);
}

一切工作正常。每次调用 web 服务都会将用户名和密码传递到标头中。您可以轻松更改此设置以使其适用于您的情况。

编辑

上面的代码使用 CXF API 来修改头部。我也使用 JAX-WS 完成了这个操作。它几乎是相同的,修改头部以添加用户凭据:

public class MyHandler implements SOAPHandler<SOAPMessageContext> {

    private static final Logger LOGGER = LoggerFactory.getLogger(MyHandler.class);

    private String username;

    private String password;

    /**
     * Handles SOAP message. If SOAP header does not already exist, then method will created new SOAP header. The
     * username and password is added to the header as the credentials to authenticate user. If no user credentials is
     * specified every call to web service will fail.
     *
     * @param context SOAP message context to get SOAP message from
     * @return true
     */
    @Override
    public boolean handleMessage(SOAPMessageContext context) {
        try {
            SOAPMessage message = context.getMessage();
            SOAPHeader header = message.getSOAPHeader();
            SOAPEnvelope envelope = message.getSOAPPart().getEnvelope();
            if (header == null) {
                header = envelope.addHeader();
            }
            QName qNameUserCredentials = new QName("https://your.target.namespace/", "UserCredentials");
            SOAPHeaderElement userCredentials = header.addHeaderElement(qNameUserCredentials);

            QName qNameUsername = new QName("https://your.target.namespace/", "Username");
            SOAPHeaderElement username = header.addHeaderElement(qNameUsername );
            username.addTextNode(this.username);
            QName qNamePassword = new QName("https://your.target.namespace/", "Password");
            SOAPHeaderElement password = header.addHeaderElement(qNamePassword);
            password.addTextNode(this.password);

            userCredentials.addChildElement(username);
            userCredentials.addChildElement(password);

            message.saveChanges();
            //TODO: remove this writer when the testing is finished
            StringWriter writer = new StringWriter();
            message.writeTo(new StringOutputStream(writer));
            LOGGER.debug("SOAP message: \n" + writer.toString());
        } catch (SOAPException e) {
            LOGGER.error("Error occurred while adding credentials to SOAP header.", e);
        } catch (IOException e) {
            LOGGER.error("Error occurred while writing message to output stream.", e);
        }
        return true;
    }

    //TODO: remove this class after testing is finished
    private static class StringOutputStream extends OutputStream {

        private StringWriter writer;

        public StringOutputStream(StringWriter writer) {
            this.writer = writer;
        }

        @Override
        public void write(int b) throws IOException {
            writer.write(b);
        }
    }

    @Override
    public boolean handleFault(SOAPMessageContext context) {
        LOGGER.debug("handleFault has been invoked.");
        return true;
    }

    @Override
    public void close(MessageContext context) {
        LOGGER.debug("close has been invoked.");
    }

    @Override
    public Set<QName> getHeaders() {
        LOGGER.debug("getHeaders has been invoked.");
        return null;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

我将此处理程序在 Spring 配置中定义如下:

<bean id="soapHandler" class="your.package.MyHandler">
    <property name="username" value="testUser"/>
    <property name="password" value="testPassword"/>
</bean>

<jaxws:client "...">
    <jaxws:handlers>
        <ref bean="soapHandler"/>
    </jaxws:handlers>
</jaxws:client>

您只需要实现SOAPHandler<SOAPMessageContext>接口,重写其方法,然后就可以对SOAP消息进行任何操作了。


谢谢您的回复。您能否发布Header类的完全限定名称?我只想找出它是否来自jaxws api。 - ikvenu2000
头文件不是来自JAX-WS,而是来自CXF,我已经编辑了答案,说明如何在JAX-WS中修改头文件。 - Paulius Matulionis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接