SOAP handler is a SOAP message interceptor, which is able to intercept incoming or outgoing SOAP message and manipulate its values. For example, attach a SOAP handler in client side, which will inject authentication credential into the SOAP header block for every outgoing SOAP message that is send by the client. In server side, attach another SOAP handler, to retrieve back the authentication credential in SOAP header block from every incoming SOAP message. In this way we can validate the incoming requests and secure our system. In this article we are going inject the authentication credential in header to access the web service. We are going to create the client to access the service explained in my previous article JAX-WS Web Service Handler (Server).
For this tutorial I will be using the following tools
- JDK 7
- Eclipse Juno
- Create the client for the web service
- Create SOAP Handler
- Handler Configuration
- Handler Mapping
- Tracing the out going and incoming messages in success and failure case.
As I have already wrote an article to demonstrate how to create web service client, I will not be explaining it again . Please refer Web Service Client to import and create web service client.
Create Soap Handler:
In order to create a SOAP handler, we have to implement javax.xml.ws.handler.soap.SOAPHandler, same way we did while writing handler for end point at server. We have to give implementation in handleMessage(). In this method we are going to populate the header with authentication credentials.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | package com.techiekernel.ws.jaxws.handler; import java.io.IOException; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.SocketException; import java.net.UnknownHostException; import java.util.Set; import javax.xml.namespace.QName; import javax.xml.soap.SOAPConstants; import javax.xml.soap.SOAPEnvelope; import javax.xml.soap.SOAPException; import javax.xml.soap.SOAPHeader; import javax.xml.soap.SOAPHeaderElement; import javax.xml.soap.SOAPMessage; import javax.xml.ws.handler.MessageContext; import javax.xml.ws.handler.soap.SOAPHandler; import javax.xml.ws.handler.soap.SOAPMessageContext; public class CredentialProvider implements SOAPHandler<SOAPMessageContext> { private static final String USER_NAME = "foo"; private static final String PASSWORD = "bar"; public boolean handleMessage(SOAPMessageContext context) { System.out.println("handleMessage() called."); Boolean isRequest = (Boolean) context .get(MessageContext.MESSAGE_OUTBOUND_PROPERTY); // if this is a request, true for outbound messages, false for inbound if (isRequest) { try { SOAPMessage soapMsg = context.getMessage(); SOAPEnvelope soapEnv = soapMsg.getSOAPPart().getEnvelope(); SOAPHeader soapHeader = soapEnv.getHeader(); // if no header, add one if (soapHeader == null) { soapHeader = soapEnv.addHeader(); } // add a soap header, username QName qnameUser = new QName("http://document.jaxws.ws.techiekernel.com/", "userName"); SOAPHeaderElement soapHeaderElementUser = soapHeader .addHeaderElement(qnameUser); soapHeaderElementUser.setActor(SOAPConstants.URI_SOAP_ACTOR_NEXT); soapHeaderElementUser.addTextNode(USER_NAME); // add a soap header, username QName qnamePassword = new QName("http://document.jaxws.ws.techiekernel.com/", "password"); SOAPHeaderElement soapHeaderElementPassword = soapHeader .addHeaderElement(qnamePassword); soapHeaderElementPassword.setActor(SOAPConstants.URI_SOAP_ACTOR_NEXT); soapHeaderElementPassword.addTextNode(PASSWORD); soapMsg.saveChanges(); // tracking soapMsg.writeTo(System.out); } catch (SOAPException e) { System.err.println(e); } catch (IOException e) { System.err.println(e); } } // continue other handler chain return true; } public boolean handleFault(SOAPMessageContext context) { System.out.println("handleFault() called."); return true; } public void close(MessageContext context) { System.out.println("close() called"); } public Set<QName> getHeaders() { System.out.println("getHeaders() called."); return null; } } |
Handler Configuration:
Let’s create define the handler in a handler mapping file and name it as handler.xml.
1 2 3 4 5 6 7 8 9 10 | <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <javaee:handler-chains xmlns:javaee="http://java.sun.com/xml/ns/javaee" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <javaee:handler-chain> <javaee:handler> <javaee:handler-class>com.techiekernel.ws.jaxws.handler.CredentialProvider</javaee:handler-class> </javaee:handler> </javaee:handler-chain> </javaee:handler-chains> |
Handler Mapping:
While importing the WSLD, some stubs will be get created at the client. To attach above SOAP handler, just annotate the service class with @HandlerChain and specify the SOAP handler file name inside. In our case following class we have to consider. We have to keep the hander configuration file i.e. handler.xml in the class path.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | package com.techiekernel.ws.jaxws.document; import java.net.MalformedURLException; import java.net.URL; import java.util.logging.Logger; import javax.xml.namespace.QName; import javax.xml.ws.Service; import javax.xml.ws.WebEndpoint; import javax.xml.ws.WebServiceClient; import javax.xml.ws.WebServiceFeature; import javax.jws.HandlerChain; /** * This class was generated by the JAX-WS RI. * JAX-WS RI 2.1.7-b01- * Generated source version: 2.1 * */ @WebServiceClient(name = "FooBarImplService", targetNamespace = "http://document.jaxws.ws.techiekernel.com/", wsdlLocation = "http://localhost:8080/webservice-JAX-WS-handler/foobar?wsdl") @HandlerChain(file="handler.xml") public class FooBarImplService extends Service { private final static URL FOOBARIMPLSERVICE_WSDL_LOCATION; private final static Logger logger = Logger.getLogger(com.techiekernel.ws.jaxws.document.FooBarImplService.class.getName()); static { URL url = null; try { URL baseUrl; baseUrl = com.techiekernel.ws.jaxws.document.FooBarImplService.class.getResource("."); url = new URL(baseUrl, "http://localhost:8080/webservice-JAX-WS-handler/foobar?wsdl"); } catch (MalformedURLException e) { logger.warning("Failed to create URL for the wsdl Location: 'http://localhost:8080/webservice-JAX-WS-handler/foobar?wsdl', retrying as a local file"); logger.warning(e.getMessage()); } FOOBARIMPLSERVICE_WSDL_LOCATION = url; } public FooBarImplService(URL wsdlLocation, QName serviceName) { super(wsdlLocation, serviceName); } public FooBarImplService() { super(FOOBARIMPLSERVICE_WSDL_LOCATION, new QName("http://document.jaxws.ws.techiekernel.com/", "FooBarImplService")); } /** * * @return * returns FooBarImpl */ @WebEndpoint(name = "FooBarImplPort") public FooBarImpl getFooBarImplPort() { return super.getPort(new QName("http://document.jaxws.ws.techiekernel.com/", "FooBarImplPort"), FooBarImpl.class); } /** * * @param features * A list of {@link javax.xml.ws.WebServiceFeature} to configure on the proxy. Supported features not in the <code>features</code> parameter will have their default values. * @return * returns FooBarImpl */ @WebEndpoint(name = "FooBarImplPort") public FooBarImpl getFooBarImplPort(WebServiceFeature... features) { return super.getPort(new QName("http://document.jaxws.ws.techiekernel.com/", "FooBarImplPort"), FooBarImpl.class, features); } } |
Tracing SOAP Messages:
Let's first see how the SOAP message looks in case of a success interaction.
Client Request SOAP Message:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"> <S:Header> <userName xmlns="http://document.jaxws.ws.techiekernel.com/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" SOAP-ENV:actor="http://schemas.xmlsoap.org/soap/actor/next">foo</userName> <password xmlns="http://document.jaxws.ws.techiekernel.com/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" SOAP-ENV:actor="http://schemas.xmlsoap.org/soap/actor/next">bar</password> </S:Header> <S:Body> <ns2:callFooBar xmlns:ns2="http://document.jaxws.ws.techiekernel.com/"> <arg0>Satish</arg0> </ns2:callFooBar> </S:Body> </S:Envelope> |
Server Response SOAP Message:
1 2 3 4 5 6 7 8 | <S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"> <S:Header /> <S:Body> <ns2:callFooBar xmlns:ns2="http://document.jaxws.ws.techiekernel.com/"> <arg0>Satish</arg0> </ns2:callFooBar> </S:Body> </S:Envelope> |
Now let change the password to something else to check, how the handler works.
Client Request SOAP Message:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"> <S:Header> <userName xmlns="http://document.jaxws.ws.techiekernel.com/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" SOAP-ENV:actor="http://schemas.xmlsoap.org/soap/actor/next">foo</userName> <password xmlns="http://document.jaxws.ws.techiekernel.com/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" SOAP-ENV:actor="http://schemas.xmlsoap.org/soap/actor/next">bar1</password> </S:Header> <S:Body> <ns2:callFooBar xmlns:ns2="http://document.jaxws.ws.techiekernel.com/"> <arg0>Satish</arg0> </ns2:callFooBar> </S:Body> </S:Envelope> |
Server Response SOAP Message:
1 2 3 4 5 6 7 8 9 10 11 12 | <S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"> <S:Header /> <S:Body> <ns2:callFooBar xmlns:ns2="http://document.jaxws.ws.techiekernel.com/"> <arg0>Satish</arg0> </ns2:callFooBar> <S:Fault> <faultcode>S:Server</faultcode> <faultstring>Authentication Failed</faultstring> </S:Fault> </S:Body> </S:Envelope> |
Source Code:
You can pull the code from GitHub. The shared code contains the full implementation.
0 comments:
Post a Comment