jws

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

Nikos Maravitsas

Nikos has graduated from the Department of Informatics and Telecommunications of The National and Kapodistrian University of Athens. During his studies he discovered his interests about software development and he has successfully completed numerous assignments in a variety of fields. Currently, his main interests are system’s security, parallel systems, artificial intelligence, operating systems, system programming, telecommunications, web applications, human – machine interaction and mobile development.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button