36

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

我该如何创建这个令牌?

4

10 回答 10

41

可以使用 @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);
于 2012-12-09T09:14:11.907 回答
34

不是 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-based Web 服务实现 WS-Security UsernameToken Profile的精彩帖子(跳转到第 4 步)。在这一步中使用 NetBeans 可能会使事情变得容易很多。

于 2010-02-24T01:47:53.710 回答
7

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

于 2012-09-17T20:46:34.770 回答
7

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

我必须向 Proxy 添加一个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());
于 2016-04-25T17:54:40.300 回答
4

您可以将用户名和密码添加到 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);
于 2017-06-22T11:15:57.287 回答
3

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

于 2016-03-11T18:46:59.960 回答
2

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

...

我希望这可以帮助你

于 2018-10-17T15:19:53.167 回答
0

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

因此,Map<String, List<String>>带有键的 requestContextMessageContext.HTTP_REQUEST_HEADERS将被复制到 SOAP 标头。通过标头使用 JAX-WS的应用程序身份验证示例

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

另请参见JAX-WS 中的消息上下文

于 2017-02-04T16:00:55.150 回答
0

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

edubriguenti 的回答让我很接近。不过,处理程序在最后一段代码中的连接方式对我不起作用——它从未被调用过。

我最终使用了他的处理程序类的一个变体,但是将它连接到这样的javax.xml.ws.Service实例中:

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

于 2018-10-04T03:27:22.400 回答
-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());

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

于 2020-09-17T14:55:59.483 回答