5

I need to incorporate an authentication header (i.e. as a part of SOAP header request) in my new web service. That authentication header will verify the userId and password details. I have to verify the content of request header details for authentication in my Web Service. If authenticated, then the SOAP body of the request will be processed, else Invalid Authentication message will be send back by the Web Service to the client application invoking the service.

I am not able to understand how to create a web service where the SOAP Header will contain some elements(in my case, authentication elements such as userId and password).

Normally, whatever method exposed in the service will come as a part of the SOAP Body. Hence confused how to proceed with adding authentication elements in the SOAP Header.

Please help

Regards,

user182944
  • 7,897
  • 33
  • 108
  • 174
  • Can anyone look at this? https://stackoverflow.com/questions/43002576/soap-header-xmlnsds-on-each-element-for-xml-signature-generation – noob'88 Mar 31 '17 at 20:41

2 Answers2

5

Recently I have wrote a class which adds user credentials to SOAP header. To do that you need to create a class which implements SOAPHandler<SOAPMessageContext> interface. For e.g.:

public class MyHandler implements SOAPHandler<SOAPMessageContext> {

    private static final Logger LOGGER = LoggerFactory.getLogger(MyHandler.class);

    private String username;

    private String password;

    /**
     * Handles SOAP message. If SOAP header does not already exist, then method will created new SOAP header. The
     * username and password is added to the header as the credentials to authenticate user. If no user credentials is
     * specified every call to web service will fail.
     *
     * @param context SOAP message context to get SOAP message from
     * @return true
     */
    @Override
    public boolean handleMessage(SOAPMessageContext context) {
        try {
            SOAPMessage message = context.getMessage();
            SOAPHeader header = message.getSOAPHeader();
            SOAPEnvelope envelope = message.getSOAPPart().getEnvelope();
            if (header == null) {
                header = envelope.addHeader();
            }
            QName qNameUserCredentials = new QName("https://your.target.namespace/", "UserCredentials");
            SOAPHeaderElement userCredentials = header.addHeaderElement(qNameUserCredentials);

            QName qNameUsername = new QName("https://your.target.namespace/", "Username");
            SOAPHeaderElement username = header.addHeaderElement(qNameUsername );
            username.addTextNode(this.username);
            QName qNamePassword = new QName("https://your.target.namespace/", "Password");
            SOAPHeaderElement password = header.addHeaderElement(qNamePassword);
            password.addTextNode(this.password);

            userCredentials.addChildElement(username);
            userCredentials.addChildElement(password);

            message.saveChanges();
            //TODO: remove this writer when the testing is finished
            StringWriter writer = new StringWriter();
            message.writeTo(new StringOutputStream(writer));
            LOGGER.debug("SOAP message: \n" + writer.toString());
        } catch (SOAPException e) {
            LOGGER.error("Error occurred while adding credentials to SOAP header.", e);
        } catch (IOException e) {
            LOGGER.error("Error occurred while writing message to output stream.", e);
        }
        return true;
    }

    //TODO: remove this class after testing is finished
    private static class StringOutputStream extends OutputStream {

        private StringWriter writer;

        public StringOutputStream(StringWriter writer) {
            this.writer = writer;
        }

        @Override
        public void write(int b) throws IOException {
            writer.write(b);
        }
    }

    @Override
    public boolean handleFault(SOAPMessageContext context) {
        LOGGER.debug("handleFault has been invoked.");
        return true;
    }

    @Override
    public void close(MessageContext context) {
        LOGGER.debug("close has been invoked.");
    }

    @Override
    public Set<QName> getHeaders() {
        LOGGER.debug("getHeaders has been invoked.");
        return null;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

Note that I am just adding the credentials to the header and returning true. You do what ever you want with whole message and return false if something that is expected fails.

I have implemented this one the client:

<bean id="soapHandler" class="your.package.MyHandler">
    <property name="username" value="testUser"/>
    <property name="password" value="testPassword"/>
</bean>

<jaxws:client "...">
    <jaxws:handlers>
        <ref bean="soapHandler"/>
    </jaxws:handlers>
</jaxws:client>

But it also can be implemented on the endpoint.

Paulius Matulionis
  • 23,085
  • 22
  • 103
  • 143
  • Yes it comes from javax.xml.ws.handler.soap package. I do not know if it will work with JAX-RPC, I only used it with JAX-WS. You should try and see if it works :) – Paulius Matulionis Sep 06 '12 at 16:27
  • 1
    Here is the link you should read about JAX-RPC handler with IBM: http://www.ibm.com/developerworks/webservices/library/ws-tipjax2/index.html – Paulius Matulionis Sep 06 '12 at 16:28
  • If you have a handler and you can receive the message into that handler, that means that you can do anything to the message. You just need figure out how to modify the message of RPC style. Here is the examples how it is done via JAX-RPC: http://www.ibm.com/developerworks/xml/library/ws-tip-extend/index.html – Paulius Matulionis Sep 07 '12 at 15:05
  • I don't know anything about RPC because I haven't used it. I suggest you to consider taking a look at Apache CXF: http://cxf.apache.org/docs/writing-a-service-with-spring.html. It is the newest web service stack. It is based on JAX-WS but has a lot of other features which sometimes are very useful. – Paulius Matulionis Sep 07 '12 at 15:22
  • Yes there is, you need to declare minOccurs="1" in your WSDL element. For e.g.: . This will tell the stack that this element must occur at least one time. – Paulius Matulionis Sep 11 '12 at 14:30
  • Which approach are you using? WSDL-First, or Java-First? If you are using WSDL-first we have to add those manually, because you writing your WSDL by hand, and if Java-first, then I don't know, you have do some searching. – Paulius Matulionis Sep 11 '12 at 14:45
  • 1
    If you would be using JAX-WS there a lot of documentation on how to make this work. The solution with JAX-WS is to annotate your java parameter: @XmlElement(required=true). But I don't know about JAX-RPC – Paulius Matulionis Sep 11 '12 at 15:02
  • In the above posted code, the first parameter for QName is https namespace but for me the target namespace is http :( From where is this https concept coming? Please help – user182944 Sep 15 '12 at 13:49
0

We can get header from the envelop only not from soap message.

Krishna Kumar Chourasiya
  • 2,030
  • 3
  • 20
  • 23