JAX-WS - 添加SOAP标头

时间:2010-02-24 00:57:22

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

我正在尝试创建一个独立的客户端来使用一些Web服务。我必须将我的用户名和密码添加到SOAP Header中。我尝试添加凭据如下:

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.

如何创建此令牌?

10 个答案:

答案 0 :(得分:31)

抱歉我的英语不好。可以使用@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 :(得分:27)

不是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可能会减轻很多事情。

答案 2 :(得分: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

答案 3 :(得分:2)

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

答案 4 :(得分:1)

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

我必须在代理中添加标头处理程序

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

答案 5 :(得分:1)

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

答案 6 :(得分: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>>的requestContext中的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

答案 7 :(得分:0)

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

答案 8 :(得分:0)

最好的选择(当然,对我而言)是yourserfl。这意味着您可以以编程方式修改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" );

...

希望对您有帮助

答案 9 :(得分:0)

在标头中添加一个对象,我们使用此处使用的示例,但我将完成

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

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