【问题标题】:JAX-WS - Adding SOAP HeadersJAX-WS - 添加 SOAP 标头
【发布时间】:2011-01-20 08:17:48
【问题描述】:

我正在尝试创建一个独立的客户端来使用一些 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.

我该如何创建这个令牌?

【问题讨论】:

  • 目前正在使用 SOAP 并遇到同样的问题。在阅读了这个问题的所有答案之后,我有一种想要退出编程的感觉,我喜欢这个。老实说,我理解为什么现在每个人都在使用 REST,以及为什么 SOAP 是一种本不应该存在的可怕技术......
  • 特别感谢@hello_earth,他指出了其他人只会提供错误线索的确切地方。

标签: java web-services soap jax-ws ws-security


【解决方案1】:

将对象添加到标题我们使用这里使用的示例,但我会完成

  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());

这会将对象添加到标题中。

【讨论】:

    【解决方案2】:

    可以使用@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 Headers 的客户端,则需要使用 -XadditionalHeaders:

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

    如果不需要@Oneway 网络服务,可以使用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);
    

    【讨论】:

    • +1,-XadditionalHeaders 在这种情况下是一个重要的属性。
    • header=true 对我有用。我已经复制了现有的存根并在副本上设置了 header = true 以便 maven wsimport 不要覆盖生成的存根。
    • maven 插件中 -additionalHeader 的等价物是什么?
    • @AbdulRazakAK -XadditionalHeaders
    • 对于那些使用wsdl2java 而不是wsimport 的人,-XadditionalHeaders 的等价物是-exsh true(exsh 代表扩展soap 标头绑定,'true' 启用此功能)带有示例命令存在:.\wsdl2java.bat -exsh true -autoNameResolution &lt;wsdl-url&gt;
    【解决方案3】:

    最好的选择(当然对我来说)是自己动手。这意味着您可以通过编程方式修改 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" );
    

    ...

    希望对你有帮助

    【讨论】:

      【解决方案4】:

      我从Pascal's solution 开始在这里的所有答案中苦苦挣扎,随着Java 编译器不再默认绑定rt.jar(并且使用内部类使其特定于该运行时实现),这变得越来越困难。

      The answer from edubriguenti 把我拉近了。不过,处理程序在最后一段代码中的连接方式对我不起作用——它从未被调用过。

      我最终使用了他的处理程序类的变体,但将其连接到 javax.xml.ws.Service 实例,如下所示:

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

      【讨论】:

        【解决方案5】:

        不能 100% 确定,因为问题缺少一些细节,但如果您使用的是 JAX-WS RI,请查看 Adding SOAP headers when sending requests

        这样做的便携方式是 你创建了一个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 的保护。这与您最初的问题有点不同。无论如何,要配置您的客户端以发送用户名和密码,我建议查看伟大的帖子Implementing the WS-Security UsernameToken Profile for Metro-based web services(跳转到第 4 步)。在这一步中使用 NetBeans 可能会轻松很多。

        【讨论】:

        • 我无法让 eclipse 导入这个 com.sun.xml.internal.ws.developer.WSBindingProvider 类。
        • 如果我们使用 com.sum 包中的类会不会有任何可移植性问题?
        • 关于 WS-Security 的帖子链接已失效。
        • @pihentagy - 我有import javax.xml.ws.BindingProvider;
        【解决方案6】:

        您可以将用户名和密码添加到 SOAP 标头

        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);
        

        【讨论】:

        • 到目前为止,最简单的解决方案。 PD:我使用Maven没有上面提到的任何问题,不需要&lt;args&gt;-XadditionalHeaders&lt;/args&gt;,没有额外的依赖
        • 如果需要 UsernameToken 这将不起作用 - 这会修改 HTTP 标头,而不是 SOAP 消息标头...
        • 虽然在 UsernameToken 场景中也可能需要基本身份验证标头......
        【解决方案7】:

        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);
        

        因此,来自 requestContext 的 Map&lt;String, List&lt;String&gt;&gt; 键为 MessageContext.HTTP_REQUEST_HEADERS 将被复制到 SOAP 标头。 Application Authentication with JAX-WS via headers的样本

        BindingProvider.USERNAME_PROPERTYBindingProvider.PASSWORD_PROPERTY 密钥在HttpTransportPipe.addBasicAuth() 中以特殊方式处理,添加标准基本授权Authorization 标头。

        另见Message Context in JAX-WS

        【讨论】:

          【解决方案8】:

          此外,如果您使用 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

          【讨论】:

            【解决方案9】:

            我添加这个答案是因为其他人都没有为我工作。

            我必须向代理添加一个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) {
                    //
                }
            }
            

            在代理中,我只是添加了Handler:

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

            【讨论】:

            • 请注意,以这种方式添加处理程序是行不通的,因为需要调用 bp.getBinding().setHandlerChain(...) - 请参阅 rumberomelo 的回答
            • 还有一个专门针对 UsernameToken 标头场景的更好示例:ibm.com/docs/en/sc-and-ds/…
            • 感谢 @hello_earth,这 2 个 cmets 正是解决这个问题所需要的。
            【解决方案10】:

            使用 maven 和插件 jaxws-maven-plugin。这将生成一个 Web 服务客户端。确保将 xadditionalHeaders 设置为 true。这将生成带有标题输入的方法。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2010-10-24
              • 1970-01-01
              • 2011-05-02
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2015-07-19
              相关资源
              最近更新 更多