Authentication in Restful Webservices in Java Example

13 May 2015

Introduction

This document will help user to setup a RESTful webservice with Basic HTTP authentication powered by Jersey framework. You shall get lots of blogs discuss about how to write RESTful webservice? But there are a few that will cover Authentication of RESTful webservice.

Required software

  • J2EE eclipse (e.g. Eclipse Kepler)
  • JDK 1.7
  • Maven. If you want to know more about maven setup then follow my blog on maven

Objectives

Write a restful webservice that expectes authentication token in the header of the request. If the request does not contain authentication parameter then the web service call should fail. A test client java code is used to test the RESTful webservice. Backend developers always prefer testing RESTful webservice code using test client java code which is faster and convenient way of testing.

Steps to write a code

Create a RESTful project

                          <project xmlns="http://maven.apache.org/POM/4.0.0"  	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd http://maven.apache.org/xsd/maven-v4_0_0.xsd">   <modelVersion>4.0.0</modelVersion>   <groupId>com.ashish.rest.controller</groupId>   <artifactId>RESTfulAuth</artifactId>   <packaging>war</packaging>   <version>0.0.1-SNAPSHOT</version> 	 	<properties> 		<spring.version>4.0.1.RELEASE</spring.version> 	</properties>   <repositories> 		<repository> 			<id>maven2-repository.java.net</id> 			<name>Java.net Repository for Maven</name> 			<url>http://download.java.net/maven/2/</url> 			<layout>default</layout> 		</repository> 	</repositories>    	<build> 		<sourceDirectory>src</sourceDirectory> 		<plugins> 			<plugin> 				<artifactId>maven-compiler-plugin</artifactId> 				<version>3.1</version> 				<configuration> 					<source>1.7</source> 					<target>1.7</target> 				</configuration> 			</plugin> 			<plugin> 				<artifactId>maven-war-plugin</artifactId> 				<version>2.4</version> 				<configuration> 					<warSourceDirectory>WebContent</warSourceDirectory> 					<failOnMissingWebXml>false</failOnMissingWebXml> 				</configuration> 			</plugin> 		</plugins> 	</build>   	<dependencies> 				 		<dependency> 		  <groupId>commons-codec</groupId> 		  <artifactId>commons-codec</artifactId> 		  <version>1.9</version> 		</dependency>   		<!-- Below dependency is for the jersey framework for RESTful web service--> 		<dependency> 			<groupId>com.sun.jersey</groupId> 			<artifactId>jersey-server</artifactId> 			<version>1.9</version> 		</dependency> 		 		<!--  Below two dependencies are added to support JSON response -->  		<dependency> 		    <groupId>com.sun.jersey</groupId> 		    <artifactId>jersey-json</artifactId> 		    <version>1.8</version> 		 </dependency> 		<dependency>  			<groupId>com.sun.jersey</groupId>  			<artifactId>jersey-bundle</artifactId>  			<version>1.18.1</version>  		</dependency> 		 		<dependency> 		    <groupId>javax.servlet</groupId> 		    <artifactId>javax.servlet-api</artifactId> 		    <version>3.0.1</version> 		    <scope>provided</scope> 		</dependency> 	</dependencies> </project>                      
  • web.xml: All http requests must pass through com.ashish.rest.authentication.RestAuthenticationFilter authentication class.
                          <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">   <display-name>Restful WebApplication</display-name>  <welcome-file-list>  	<welcome-file>index.jsp</welcome-file>  </welcome-file-list> 	<servlet> 		<servlet-name>jersey-helloWorld-serlvet</servlet-name> 		<servlet-class>             com.sun.jersey.spi.container.servlet.ServletContainer         </servlet-class>         <!-- Below init-param is added to for RESTful webservice with Jersey framework --> 		<init-param> 		     <param-name>com.sun.jersey.config.property.packages</param-name> 		     <param-value>com.ashish.rest.controller</param-value> 		</init-param> 		<!-- Below init-param is added to support JSON response --> 		<init-param>             <param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>             <param-value>true</param-value>         </init-param> 		<load-on-startup>1</load-on-startup> 	</servlet>   	<servlet-mapping> 		<servlet-name>jersey-helloWorld-serlvet</servlet-name> 		<url-pattern>/rest/*</url-pattern> 	</servlet-mapping> 	 	<filter> 	    <filter-name>AuthenticationFilter</filter-name> 	    <filter-class>com.ashish.rest.authentication.RestAuthenticationFilter</filter-class> 	</filter> 	<filter-mapping> 		<filter-name>AuthenticationFilter</filter-name> 		<url-pattern>/rest/*</url-pattern> 	</filter-mapping> </web-app>                      
  • A GET and POST request shown here which will return JSON output for a employee. This method takes employeeId as input URL is : http://localhost:8080/RESTfulAuth/rest/hello/getEmployee/123
                          package com.ashish.rest.controller; import javax.ws.rs.GET; ..... @Path("/hello") public class HelloWorldREST {   	@GET 	@Path("/getEmployee/{empId}") 	@Produces(MediaType.APPLICATION_JSON) 	public Employee getEmployee( @PathParam("empId") int empId, 			@DefaultValue("No Employee Id passed") @QueryParam("value") String value) { 		System.out.println("getEmployee method is called"); 		Employee emp = new Employee(); 		emp.setEmpId(empId); 		emp.setName("Ashish Mondal"); 	 		return emp; 	 	} 	 	@POST 	@Path("/getSalary") 	@Produces(MediaType.APPLICATION_JSON) 	public Employee getSalary( @PathParam("empId") int empId, 			@DefaultValue("No Employee Id passed") @QueryParam("value") String value) { 		System.out.println("getSalary method is called"); 		Employee emp = new Employee(); 		emp.setEmpId(empId); 		emp.setName("Ashish Mondal"); 		emp.setSalary(1000); 		 		return emp;  	} }                      
  • Once you hit http://localhost:8080/RESTfulAuth/rest/hello/getEmployee/123 URL you will see 401 error this means your HTTP basic authentication is working as expected.
  • Write the below RestAuthenticationFilter Java class to pass the REST request through Basic authentication class
                          public class RestAuthenticationFilter implements javax.servlet.Filter { 	public static final String AUTHENTICATION_HEADER = "Authorization";  	@Override 	public void doFilter(ServletRequest request, ServletResponse response, 			FilterChain filter) throws IOException, ServletException { 		if (request instanceof HttpServletRequest) { 			HttpServletRequest httpServletRequest = (HttpServletRequest) request; 			String authCredentials = httpServletRequest 					.getHeader(AUTHENTICATION_HEADER);  			// You can implement dependancy injection here 			AuthenticationService authenticationService = new AuthenticationService();  			boolean authenticationStatus = authenticationService 					.authenticate(authCredentials);  			if (authenticationStatus) { 				filter.doFilter(request, response); 			} else { 				if (response instanceof HttpServletResponse) { 					HttpServletResponse httpServletResponse = (HttpServletResponse) response; 					httpServletResponse 							.setStatus(HttpServletResponse.SC_UNAUTHORIZED); 				} 			} 		} 	}  	@Override 	public void destroy() { 	}  	@Override 	public void init(FilterConfig arg0) throws ServletException { 	} }                      
  • In the above code the authentication is done in AuthenticationService.authenticate() method as shown below
                          package com.ashish.rest.authentication.service;  import java.io.IOException; import java.util.StringTokenizer; import sun.misc.BASE64Decoder;  public class AuthenticationService { 	public boolean authenticate(String credential) { 		if (null == credential) { 			return false; 		} 		// header value format will be "Basic encodedstring" for Basic 		// authentication. Example "Basic YWRtaW46YWRtaW4=" 		final String encodedUserPassword = credential.replaceFirst("Basic" + " ", ""); 		String usernameAndPassword = null; 		try { 			byte[] decodedBytes = new BASE64Decoder().decodeBuffer(encodedUserPassword); 			usernameAndPassword = new String(decodedBytes, "UTF-8"); 		} catch (IOException e) { 			e.printStackTrace(); 		} 		final StringTokenizer tokenizer = new StringTokenizer(usernameAndPassword, ":"); 		final String username = tokenizer.nextToken(); 		final String password = tokenizer.nextToken();  		// we have fixed the userid and password as admin 		// call some UserService/LDAP here 		boolean authenticationStatus = "admin".equals(username) && "admin".equals(password); 		return authenticationStatus; 	} }                      
  • Write below client code to test your code. Below code can test the following
    • Test POST request without passing authentication request in the header
    • Test POST request with passing authentication request in the header
    • Test GET request with passing authentication request in the header
                          package com.ashish.rest.test;  import sun.misc.BASE64Encoder;  import com.sun.jersey.api.client.Client; import com.sun.jersey.api.client.ClientResponse; import com.sun.jersey.api.client.WebResource;  public class TestClient {    public static void main(String[] args) {  	  testPostWithoutBasicAuth(); 	  testPOSTWithBasicAuth(); 	  testGETWithBasicAuth();  }    /**    * Below method is used to test GET request with HTTP Basic authentication in the header of the request    */ 	private static void testGETWithBasicAuth() { 		try { 	 	        Client client = Client.create(); 	 	        String name = "admin"; 	        String password = "admin"; 	        String authString = name + ":" + password; 	        String authStringEnc = new BASE64Encoder().encode(authString.getBytes()); 	        System.out.println("Base64 encoded auth string: " + authStringEnc); 	        WebResource webResource = client.resource("http://localhost:8080/RESTfulAuth/rest/hello/getEmployee/123"); 	         	        ClientResponse resp = webResource.accept("application/json") 	                                         .header("Authorization", "Basic " + authStringEnc) 	                                         .get(ClientResponse.class); 	        if(resp.getStatus() != 200){ 	            System.err.println("Unable to connect to the server"); 	        } 	        String output = resp.getEntity(String.class); 	        System.out.println("Response for the GET with HTTP Basic authentication request: "+output); 	 	      } catch (Exception e) { 	 	        e.printStackTrace(); 	 	      } finally { 	    	  System.out.println("========================================================================="); 	      } 	} 	 	/** 	 * Below method is used to test POST request with HTTP Basic authentication in the header of the request 	 */ 	private static void testPOSTWithBasicAuth() { 		try { 	 	        Client client = Client.create(); 	 	        String name = "admin"; 	        String password = "admin"; 	        String authString = name + ":" + password; 	        String authStringEnc = new BASE64Encoder().encode(authString.getBytes()); 	        System.out.println("Base64 encoded auth string: " + authStringEnc); 	        WebResource webResource = client.resource("http://localhost:8080/RESTfulAuth/rest/hello/getSalary"); 	         	        ClientResponse resp = webResource.accept("application/json") 	                                         .header("Authorization", "Basic " + authStringEnc) 	                                         .post(ClientResponse.class); 	        if(resp.getStatus() != 200){ 	            System.err.println("Unable to connect to the server"); 	        } 	        String output = resp.getEntity(String.class); 	        System.out.println("Response for the POST with HTTP Basic authentication request: "+output); 	      } catch (Exception e) { 	        e.printStackTrace(); 	      } finally { 	    	  System.out.println("========================================================================="); 	      } 	} 	 	/** 	 * Below method is used to test POST request without HTTP Basic authentication in the header of the request 	 */ 	private static void testPostWithoutBasicAuth() { 		try { 	 	        Client client = Client.create(); 	        WebResource webResource = client.resource("http://localhost:8080/RESTfulAuth/rest/hello/getSalary"); 	 	        String input = "{\"empId\":\"123\"}"; 	 	        ClientResponse response = webResource.type("application/json") 	           .post(ClientResponse.class, input); 	 	        if (response.getStatus() != 201) { 	            throw new RuntimeException("Failed : HTTP error code : " 	                 + response.getStatus()); 	        } 	 	        System.out.println("HTTP Basic authentication error .... \n"); 	        String output = response.getEntity(String.class); 	        System.out.println(output); 	      } catch (Exception e) { 	        e.printStackTrace(); 	      } finally { 	    	  System.out.println("========================================================================="); 	      } 	} }                      

The output of the above code is as shown below. First method in the above example does not pass authentication token in the request header so the calling has failed. However, other two request with the authentication string in the header has got the successful output.

              java.lang.RuntimeException: Failed : HTTP error code : 401 	at com.ashish.rest.test.TestClient.testPostWithoutBasicAuth(TestClient.java:96) 	at com.ashish.rest.test.TestClient.main(TestClient.java:13) ========================================================================= Base64 encoded auth string: YWRtaW46YWRtaW4= Response for the POST with HTTP Basic authentication request: {"empId":0,"address1":null,"address2":null,"address3":null,"pin":null,"salary":1000.0,"name":"Ashish Mondal"} ========================================================================= Base64 encoded auth string: YWRtaW46YWRtaW4= Response for the GET with HTTP Basic authentication request: {"empId":123,"address1":null,"address2":null,"address3":null,"pin":null,"salary":0.0,"name":"Ashish Mondal"} =========================================================================                          

Common issues

SL NO Issues Solution
1 Unable to load class while running a java class from eclipse You may need to update maven project by the following option (right click on the project->Maven->update maven project)
  • java-web service 3
  • Webservice Tutorial 3


blog comments powered by

J2EE,SOAP,RESTful,SVN,PMD,SONAR,JaCoCo,HTTP,API,MAVEN,AngularJS,GitHub,LDAP,AOP,ORM,JMS,MVC,AWS,SQL,PHP,H2DB,JDBC

martyncatiche.blogspot.com

Source: https://ashismo.github.io/java-web%20service/2015/05/13/RESTful-webservice-with-Basic-HTTP-authentication

0 Response to "Authentication in Restful Webservices in Java Example"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel