JAX-WS - Adding SOAP Headers

121,335

Solution 1

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.

Solution 2

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

Solution 3

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

Solution 4

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

Solution 5

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);
Share:
121,335
Jordan Allan
Author by

Jordan Allan

Updated on September 18, 2020

Comments

  • Jordan Allan
    Jordan Allan over 3 years

    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?

  • Buhake Sindi
    Buhake Sindi over 11 years
    +1, the -XadditionalHeaders is an important attribute in this case.
  • kkessell
    kkessell about 11 years
    I cannot get eclipse to import this com.sun.xml.internal.ws.developer.WSBindingProvider class.
  • james2611nov
    james2611nov about 9 years
    Will there be any portability issues if we use the classes from com.sum package?
  • Zeus
    Zeus over 8 years
    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.
  • Abdul Razak AK
    Abdul Razak AK about 6 years
    What is the equivalent in maven plugin for -additionalHeader ?
  • apetrelli
    apetrelli almost 6 years
    @AbdulRazakAK <args>-XadditionalHeaders</args>
  • vab2048
    vab2048 almost 5 years
    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>
  • FiruzzZ
    FiruzzZ over 4 years
    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 dependencies
  • Søren Boisen
    Søren Boisen over 3 years
    Link to post about WS-Security is dead.
  • JGlass
    JGlass almost 3 years
    @pihentagy - I have import javax.xml.ws.BindingProvider;
  • hello_earth
    hello_earth almost 3 years
    this wouldn't work if UsernameToken is required - this modifies the HTTP headers, not the SOAP message headers...
  • hello_earth
    hello_earth almost 3 years
    note that adding handlers this way won't work, because bp.getBinding().setHandlerChain(...) needs to be called - see rumberomelo's answer
  • hello_earth
    hello_earth almost 3 years
    also here's a better example specifically for UsernameToken headers scenario: ibm.com/docs/en/sc-and-ds/…
  • hello_earth
    hello_earth almost 3 years
    although Basic Authentication headers might also be required in UsernameToken scenario too....
  • SavinI
    SavinI over 2 years
    From which package you get Header class?
  • nephewtom
    nephewtom over 2 years
    Thanks @hello_earth, those 2 comments are exactly what it was need to bring light to this issue.