JAX-WS - 添加 SOAP 标头

发布于 2024-08-22 21:57:42 字数 901 浏览 8 评论 0原文

我正在尝试创建一个独立的客户端来使用一些网络服务。我必须将我的用户名和密码添加到 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.

如何创建此令牌?

I am trying to create a standalone client to consume some web services. I must add my username and password to the SOAP Header. I tried adding the credentials as follows:

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

...

When I call a method on the service I get the following exception:

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

What am I doing wrong? How would I add these properties to the SOAP Header?

Edited: I was using JAX-WS 2.1 included in JDK6. I am now using JAX-WS 2.2. I now get the following exception:

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.

How do I go about creating this token?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

⒈起吃苦の倖褔 2024-08-29 21:57:42

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

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

如果不需要 @Oneway Web 服务,则可以使用支架:

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

Data can be transferred in SOAP header (JaxWS) by using @WebParam(header = true):

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

If you want to generate a client with SOAP Headers, you need to use -XadditionalHeaders:

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

If don't need @Oneway web service, you can use 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);
反差帅 2024-08-29 21:57:42

不是 100% 确定,因为问题缺少一些细节,但如果您使用的是 JAX-WS RI,请查看 发送请求时添加 SOAP 标头

执行此操作的可移植方法是
你创建了一个 SOAPHandler 并搞砸了
与 SAAJ,但 RI 提供了
更好的方法来做到这一点。

当您创建代理或调度时
对象,他们实现
BindingProvider 接口。当你
使用 JAX-WS RI,您可以向下转换为
WSBindingProvider 定义了一个
仅由
JAX-WS RI。

此界面可让您设置
任意数量的 Header 对象,
每个代表一个 SOAP 标头。你
如果您可以自己实现
想要,但很可能你会使用其中之一
定义的工厂方法
Headers 类来创建一个。

导入 com.sun.xml.ws.developer.WSBindingProvider;

HelloPort 端口 = helloService.getHelloPort(); // 或者类似的东西...
WSBindingProvider bp = (WSBindingProvider)端口;

bp.setOutboundHeader(
  // 简单字符串值作为标头,例如 stringValue;
  headers.create(new QName("simpleHeader"),"stringValue"),
  // 从 JAXB 对象创建标头
  headers.create(jaxbContext,myJaxbObject)
);

相应地更新您的代码并重试。如果您没有使用 JAX-WS RI,请更新您的问题并提供更多上下文信息。

更新:您要调用的 Web 服务似乎受到 WS-Security/UsernameTokens 的保护。这与您最初的问题有点不同。无论如何,要配置您的客户端发送用户名和密码,我建议查看这篇精彩的文章 为基于 Metro 的 Web 服务实现 WS-Security UsernameToken 配置文件(跳至步骤 4)。使用 NetBeans 执行此步骤可能会使事情变得容易很多。

Not 100% sure as the question is missing some details but if you are using JAX-WS RI, then have a look at Adding SOAP headers when sending requests:

The portable way of doing this is that
you create a SOAPHandler and mess
with SAAJ, but the RI provides a
better way of doing this.

When you create a proxy or dispatch
object, they implement
BindingProvider interface. When you
use the JAX-WS RI, you can downcast to
WSBindingProvider which defines a
few more methods provided only by the
JAX-WS RI.

This interface lets you set an
arbitrary number of Header object,
each representing a SOAP header. You
can implement it on your own if you
want, but most likely you'd use one of
the factory methods defined on
Headers class to create one.

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

Update your code accordingly and try again. And if you're not using JAX-WS RI, please update your question and provide more context information.

Update: It appears that the web service you want to call is secured with WS-Security/UsernameTokens. This is a bit different from your initial question. Anyway, to configure your client to send usernames and passwords, I suggest to check the great post Implementing the WS-Security UsernameToken Profile for Metro-based web services (jump to step 4). Using NetBeans for this step might ease things a lot.

↘人皮目录ツ 2024-08-29 21:57:42

我添加这个答案是因为其他答案都不适合我。

我必须向代理添加一个标头处理程序

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

I'm adding this answer because none of the others worked for me.

I had to add a Header Handler to the Proxy:

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) {
        //
    }
}

In the proxy, I just add the Handler:

BindingProvider bp =(BindingProvider)basicHttpBindingAuthentication;
bp.getBinding().getHandlerChain().add(new SOAPHeaderHandler(authenticatedToken));
bp.getBinding().getHandlerChain().add(new SOAPLoggingHandler());
柠檬色的秋千 2024-08-29 21:57:42

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

Also, if you're using Maven to build your project, you'll need to add the following dependency:

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

This provides you with the class com.sun.xml.ws.developer.WSBindingProvider.

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

最舍不得你 2024-08-29 21:57:42

您可以将用户名和密码添加到 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);

you can add the username and password to the 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);
海风掠过北极光 2024-08-29 21:57:42

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

Use maven and the plugin jaxws-maven-plugin. this will generate a web service client. Make sure you are setting the xadditionalHeaders to true. This will generate methods with header inputs.

各自安好 2024-08-29 21:57:42

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

...

我希望这对您有帮助

The best option (for my of course) is do it yourserfl. It means you can modify programattly all parts of the SOAP message

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

And the ModifyMessageHandler source could be

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

...

I hope this helps you

陌若浮生 2024-08-29 21:57:42

在 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 的带有 MessageContext.HTTP_REQUEST_HEADERS 键的 String>> 将被复制到 SOAP 标头。
通过标头使用 JAX-WS 进行应用程序身份验证<的示例/a>

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

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

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

So, Map<String, List<String>> from requestContext with key MessageContext.HTTP_REQUEST_HEADERS will be copied to SOAP headers.
Sample of Application Authentication with JAX-WS via headers

BindingProvider.USERNAME_PROPERTY and BindingProvider.PASSWORD_PROPERTY keys are processed special way in HttpTransportPipe.addBasicAuth(), adding standard basic authorization Authorization header.

See also Message Context in JAX-WS

反话 2024-08-29 21:57:42

我为这里的所有答案而苦苦挣扎,从 Pascal 的解决方案开始,由于 Java 编译器不与 rt.jar 默认情况下不再(并且使用内部类使其特定于该运行时实现)。

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

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


Service service = Service.create(url, qname);
service.setHandlerResolver(
端口信息-> Collections.singletonList(new SOAPHeaderHandler(handlerArgs))
);

I struggled with all the answers here, starting with Pascal's solution, which is getting harder with the Java compiler not binding against rt.jar by default any more (and using internal classes makes it specific to that runtime implementation).

The answer from edubriguenti brought me close. The way the handler is hooked up in the final bit of code didn't work for me, though - it was never called.

I ended up using a variation of his handler class, but wired it into the javax.xml.ws.Service instance like this:


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

药祭#氼 2024-08-29 21:57:42

将对象添加到标头我们使用此处使用的示例,但我将

  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)

该方法将在对象 Factory 上创建一个像这样的 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());

这会将对象添加到标头。

Adding an object to header we use the examples used here,yet i will complete

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

With object factory we create the object asked to pass on the header. The to add to the header

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

We used the WSBindingProvider to add the header. The object will have some error if used directly so we use the method

objectFactory.createCabeceraCR(cabeceraCR)

This method will create a JAXBElement like this on the object Factory

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

And the jaxbContext we obtained like this:

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

This will add the object to the header.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文