Application Authentication With JAX-WS
In this example we are going to see how to implement a simple application level authentication in JAX-WS. The notion of authentication is very well covered and its necessity is quite clear. In our tutorial we are going to authenticate a client to the endpoint server.
The idea is straight forward. The client who wants to consume the Web Service, will have to add and HTTP Request Header containing his username and password. The server then gets these headers and validates the client’s credentials. Of course, passwords should be encrypted (not only hashed). In fact, all the communication should be encrypted to achieve an acceptable level of security. But in this tutorial we are going to keep things as simple as possible, but you should keep in mind that authentication along with session management are hard and crucial issues when developing a secure Web application.
The code of this tutorial is going to be based in JAX-WS Hello World Example – RPC Style.
1. Service Endpoint
In order to create our Web Service Endpoint:
- First you have to create a Web Service Endpoint Interface. This interface will contain the declerations of all the methods you want to include in the Web Service.
- Then you have to create a class that actually implements the above interface, which will be your Endpoint implementation.
Web Service Endpoint Interface (SEI)
The Web Service Endpoint Interface is similar to the perivous JAX-WS we’ve seen.
WebServiceInterface.java:
package com.javacodegeeks.enterprise.ws; import javax.jws.WebMethod; import javax.jws.WebService; import javax.jws.soap.SOAPBinding; import javax.jws.soap.SOAPBinding.Style; @WebService @SOAPBinding(style = Style.RPC) public interface WebServiceInterface{ @WebMethod String getHelloWorldAsString(String name); }
Web Service Endpoint Implementation
As we said in the introduction, the server would have to read the HTTP request headers that the client put, and validate its credetials. To access the context objects of the message, our Service Endpoint Implementation has to obtain a MessageContext
object through a WebServiceContext
. The WebServiceContext
interface enables a web service endpoint implementation class to access message contexts and security information of the requester. The service runtime will inject the WebServiceContext
on any field marked with @Resource
annotation. To get a MessageContext
instance, we can call the WebServiceContext
‘s getMessageContext()
method.
The HTTP Request Header will be retrieved using MessageContext's
get
method with MessageContext.HTTP_REQUEST_HEADERS
as a parameter which nominates the kind of context we want from the message. The Header will be retrieved as a Map
which is very convenient because you can specify (key,value) pairs directy. Thus, we retrieve the corespoding credentials.
WebServiceImpl.java:
package com.javacodegeeks.enterprise.ws; import java.util.List; import java.util.Map; import javax.annotation.Resource; import javax.jws.WebService; import javax.xml.ws.WebServiceContext; import javax.xml.ws.handler.MessageContext; @WebService(endpointInterface = "com.javacodegeeks.enterprise.ws.WebServiceInterface") public class WebServiceImpl implements WebServiceInterface { @Resource WebServiceContext webServiceContext; @Override public String getHelloWorldAsString(String str) { MessageContext messageContext = webServiceContext.getMessageContext(); // get request headers Map<?,?> requestHeaders = (Map<?,?>) messageContext.get(MessageContext.HTTP_REQUEST_HEADERS); List<?> usernameList = (List<?>) requestHeaders.get("username"); List<?> passwordList = (List<?>) requestHeaders.get("password"); String username = ""; String password = ""; if (usernameList != null) { username = usernameList.get(0).toString(); } if (passwordList != null) { password = passwordList.get(0).toString(); } // of course this is not real validation // you should validate your users from stored databases credentials if (username.equals("nikos") && password.equals("superpassword")) { return "Valid User :"+str; } else { return "Unknown User!"; } } }
Web Service Endpoint Publisher
WebServicePublisher.java:
package com.javacodegeeks.enterprise.ws; import javax.xml.ws.Endpoint; import com.javacodegeeks.enterprise.ws.WebServiceImpl; public class WebServicePublisher{ public static void main(String[] args) { Endpoint.publish("http://localhost:8888/webservice/helloworld", new WebServiceImpl()); } }
Now when you run the publisher the Web Service will be available to the clients, deployed in the URL:
http://localhost:8888/webservice/helloworld
And this is the wsdl file that is automatically created (published in http://localhost:8888/webservice/helloworld?wsdl
):
WSDL:
<?xml version="1.0" encoding="UTF-8"?> <!-- Published by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is JAX-WS RI 2.2.4-b01. --> <!-- Generated by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is JAX-WS RI 2.2.4-b01. --> - <definitions name="WebServiceImplService" targetNamespace="http://ws.enterprise.javacodegeeks.com/" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://ws.enterprise.javacodegeeks.com/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata" xmlns:wsp1_2="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:wsp="http://www.w3.org/ns/ws-policy" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> <types /> - <message name="getHelloWorldAsString"> <part name="arg0" type="xsd:string" /> </message> - <message name="getHelloWorldAsStringResponse"> <part name="return" type="xsd:string" /> </message> - <portType name="WebServiceInterface"> - <operation name="getHelloWorldAsString"> <input message="tns:getHelloWorldAsString" wsam:Action="http://ws.enterprise.javacodegeeks.com/WebServiceInterface/getHelloWorldAsStringRequest" /> <output message="tns:getHelloWorldAsStringResponse" wsam:Action="http://ws.enterprise.javacodegeeks.com/WebServiceInterface/getHelloWorldAsStringResponse" /> </operation> </portType> - <binding name="WebServiceImplPortBinding" type="tns:WebServiceInterface"> <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http" /> - <operation name="getHelloWorldAsString"> <soap:operation soapAction="" /> - <input> <soap:body namespace="http://ws.enterprise.javacodegeeks.com/" use="literal" /> </input> - <output> <soap:body namespace="http://ws.enterprise.javacodegeeks.com/" use="literal" /> </output> </operation> </binding> - <service name="WebServiceImplService"> - <port name="WebServiceImplPort" binding="tns:WebServiceImplPortBinding"> <soap:address location="http://localhost:8888/webservice/helloworld" /> </port> </service> </definitions>
2. Java Web Service Client
On the other side, the client has to make a new HTTP Request Header containing his credentials and add it to the SOAP message. To access and manupulate the request contexts of the message the client has to get a BindingProvider
from the service port and use getRequestContext()
method. The BindingProvider
interface enables the client to access and manipulate the associated context objects for request and response messages. The Request Context is retrieved as a Map
object. We then, need to add two properties to that object. MessageContext.ENDPOINT_ADDRESS_PROPERTY
to nominate the target service endpoint address. Next, we simply create a Map
containing our credentials. To add this object as a header we use MessageContext.HTTP_REQUEST_HEADERS
argument.
WebServiceClient.java:
package com.javacodegeeks.enterprise.ws.client; import java.net.URL; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.xml.namespace.QName; import javax.xml.ws.BindingProvider; import javax.xml.ws.Service; import javax.xml.ws.handler.MessageContext; import com.javacodegeeks.enterprise.ws.WebServiceInterface; public class WebServiceClient{ public static void main(String[] args) throws Exception { URL wsdlUrl = new URL("http://localhost:8888/webservice/helloworld?wsdl"); //qualifier name ... QName qname = new QName("http://ws.enterprise.javacodegeeks.com/", "WebServiceImplService"); Service service = Service.create(wsdlUrl, qname); WebServiceInterface sayHello = service.getPort(WebServiceInterface.class); Map<String, Object> requestContext = ((BindingProvider)sayHello).getRequestContext(); requestContext.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, "http://localhost:8888/webservice/helloworld?wsdl"); Map<String, List<String>> requestHeaders = new HashMap<String, List<String>>(); requestHeaders.put("username", Collections.singletonList("nikos")); requestHeaders.put("Password", Collections.singletonList("superpassword")); requestContext.put(MessageContext.HTTP_REQUEST_HEADERS, requestHeaders); System.out.println(sayHello.getHelloWorldAsString("- This is Java Code Geeks")); } }
The output of the above program will be:
Valid User :- This is Java Code Geeks
SOAP Trafic
Now you can take a look at the SOAP envelops so see the added HTTP Request Header.
Client Request:
GET /webservice/helloworld?wsdl HTTP/1.1 User-Agent: Java/1.7.0_17 Host: localhost:9999 Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2 Connection: keep-alive POST /webservice/helloworld?wsdl HTTP/1.1 Accept: text/xml, multipart/related Content-Type: text/xml; charset=utf-8 Password: superpassword SOAPAction: "http://ws.enterprise.javacodegeeks.com/WebServiceInterface/getHelloWorldAsStringRequest" User-Agent: JAX-WS RI 2.2.4-b01 username: nikos Host: localhost:9999 Connection: keep-alive Content-Length: 261 <?xml version="1.0" ?> <S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"> <S:Body> <ns2:getHelloWorldAsString xmlns:ns2="http://ws.enterprise.javacodegeeks.com/"> <arg0>- This is Java Code Geeks</arg0> </ns2:getHelloWorldAsString> </S:Body> </S:Envelope>
Server Response:
HTTP/1.1 200 OK Transfer-encoding: chunked Content-type: text/xml;charset=utf-8 Date: Wed, 11 Sep 2013 14:21:03 GMT 860 <?xml version="1.0" encoding="UTF-8"?><!-- Published by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is JAX-WS RI 2.2.4-b01. --><!-- Generated by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is JAX-WS RI 2.2.4-b01. --> <definitions xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsp="http://www.w3.org/ns/ws-policy" xmlns:wsp1_2="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://ws.enterprise.javacodegeeks.com/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.xmlsoap.org/wsdl/" targetNamespace="http://ws.enterprise.javacodegeeks.com/" name="WebServiceImplService"> <types></types> <message name="getHelloWorldAsString"> <part name="arg0" type="xsd:string"></part> </message> <message name="getHelloWorldAsStringResponse"> <part name="return" type="xsd:string"></part> </message> <portType name="WebServiceInterface"> <operation name="getHelloWorldAsString"> <input wsam:Action="http://ws.enterprise.javacodegeeks.com/WebServiceInterface/getHelloWorldAsStringRequest" message="tns:getHelloWorldAsString"></input> <output wsam:Action="http://ws.enterprise.javacodegeeks.com/WebServiceInterface/getHelloWorldAsStringResponse" message="tns:getHelloWorldAsStringResponse"></output> </operation> </portType> <binding name="WebServiceImplPortBinding" type="tns:WebServiceInterface"> <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="rpc"></soap:binding> <operation name="getHelloWorldAsString"> <soap:operation soapAction=""></soap:operation> <input> <soap:body use="literal" namespace="http://ws.enterprise.javacodegeeks.com/"></soap:body> </input> <output> <soap:body use="literal" namespace="http://ws.enterprise.javacodegeeks.com/"></soap:body> </output> </operation> </binding> <service name="WebServiceImplService"> <port name="WebServiceImplPort" binding="tns:WebServiceImplPortBinding"> <soap:address location="http://localhost:9999/webservice/helloworld"></soap:address> </port> </service> </definitions> 0 HTTP/1.1 200 OK Transfer-encoding: chunked Content-type: text/xml; charset=utf-8 Date: Wed, 11 Sep 2013 14:21:03 GMT 5e <?xml version="1.0" ?> <S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"> <S:Body> c7 <ns2:getHelloWorldAsStringResponse xmlns:ns2="http://ws.enterprise.javacodegeeks.com/"> <return>Valid User :- This is Java Code Geeks</return> </ns2:getHelloWorldAsStringResponse> </S:Body> </S:Envelope> 0
This was an example on Application Authentication With JAX-WS. Donwload the Eclipse project of this example : JAX-WS-AUTH.zip