JAX-WS - Adding SOAP Headers
Asked Answered
C

10

39

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?

Calista answered 24/2, 2010 at 0:57 Comment(2)
Currently working with SOAP and having this same problem. After having read all the answers in this question, I have the feeling I want to quit programming, which I love. Honestly, I understand why nowadays everyone is using REST and why SOAP is such a terrible technology that should have never existed...Hypophyge
And special thanks to @hello_earth who pointed out in the exact places where others only give false clues.Hypophyge
C
38

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.

Calen answered 24/2, 2010 at 1:47 Comment(4)
I cannot get eclipse to import this com.sun.xml.internal.ws.developer.WSBindingProvider class.Geometry
Will there be any portability issues if we use the classes from com.sum package?Melancholic
Link to post about WS-Security is dead.Dematerialize
@Geometry - I have import javax.xml.ws.BindingProvider;Evening
H
45

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);
Heirloom answered 9/12, 2012 at 9:14 Comment(6)
+1, the -XadditionalHeaders is an important attribute in this case.Sansom
header=true did the trick for me. I had make a copy the existing stub and set the header = true on the copy for the maven wsimport not to overwrite the generated stubs.Tucker
What is the equivalent in maven plugin for -additionalHeader ?Gerfen
@AbdulRazakAK <args>-XadditionalHeaders</args>Maragretmarala
For those who are using wsdl2java instead of wsimport the equivalent of -XadditionalHeaders is -exsh true (exsh stands for extended soap header binding and 'true' enables this) with an example command being: .\wsdl2java.bat -exsh true -autoNameResolution <wsdl-url>Parclose
From which package you get Header class?Stopwatch
C
38

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.

Calen answered 24/2, 2010 at 1:47 Comment(4)
I cannot get eclipse to import this com.sun.xml.internal.ws.developer.WSBindingProvider class.Geometry
Will there be any portability issues if we use the classes from com.sum package?Melancholic
Link to post about WS-Security is dead.Dematerialize
@Geometry - I have import javax.xml.ws.BindingProvider;Evening
H
8

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());
Hercules answered 25/4, 2016 at 17:54 Comment(3)
note that adding handlers this way won't work, because bp.getBinding().setHandlerChain(...) needs to be called - see rumberomelo's answerLacasse
also here's a better example specifically for UsernameToken headers scenario: ibm.com/docs/en/sc-and-ds/…Lacasse
Thanks @hello_earth, those 2 comments are exactly what it was need to bring light to this issue.Hypophyge
T
7

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

Timberlake answered 17/9, 2012 at 20:46 Comment(0)
O
6

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);
Outpouring answered 22/6, 2017 at 11:15 Comment(3)
by far, the easiest and simple solution. PD: I didn't have any of the problems mentioned above using Maven, no need for <args>-XadditionalHeaders</args>, no extra dependenciesRisley
this wouldn't work if UsernameToken is required - this modifies the HTTP headers, not the SOAP message headers...Lacasse
although Basic Authentication headers might also be required in UsernameToken scenario too....Lacasse
C
3

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.

Corollaceous answered 11/3, 2016 at 18:46 Comment(0)
U
2

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

Unipolar answered 17/10, 2018 at 15:19 Comment(0)
N
0

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

Nickel answered 4/2, 2017 at 16:0 Comment(0)
E
0

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

Enscroll answered 4/10, 2018 at 3:27 Comment(0)
C
-1

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.

Corporate answered 17/9, 2020 at 14:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.