RESTEasy Basic Authentication and Authorization Tutorial

Security is an integral part of any enterprise application. Security involves two phases i.e. authentication and authorization. Authentication verifies who you are. Authorization verifies what you are authorized to do. In this post, we will learn to build role based basic authentication/ authorization security for REST APIs.

Sections in this post:

Background information
Important classes/annotations used
Building the security interceptor
Testing authorization and authentication on REST APIs

Background information

In this post, I will attempt to solve following security problems:

  • Get username and password from http request
  • Fetch the applicable method security details
  • Verify if user is authorized to access the API
  • Return valid error codes in case of invalid access

Also, please note that I am leaving following parts on you to implement yourself:

  • Access database to fetch password, to verify against password provided in request
  • Fetch allowed roles from database for a valid user

I am reusing the code written for ETag usage demo. This project has 2 primary classes:

User.java : The model class which represents the user resource in system

@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement(name = "user")
public class User implements Serializable 
{
	@XmlAttribute(name = "id")
	private int id;
	
	@XmlAttribute(name="uri")
	private String uri;
		
	@XmlElement(name = "firstName")
	private String firstName;
	
	@XmlElement(name = "lastName")
	private String lastName;
	
	@XmlElement(name="last-modified")
	private Date lastModified;
	
	//Getters and setters
}

UserService.java : This class has GET and PUT APIs to fetch/ modify the user resource.

@Path("/user-service")
public class UserService
{
    @GET
    @Path("/users/{id}")
    public Response getUserById(@PathParam("id") int id, @Context Request req)
    {
        Response.ResponseBuilder rb = Response.ok(UserDatabase.getUserById(id));
        return rb.build();
    }
    
    @PUT
    @Path("/users/{id}")
    public Response updateUserById(@PathParam("id") int id)
    {
        //Update the User resource
        UserDatabase.updateUser(id);
        return Response.status(200).build();
    }
}

I will be using above 2 APIs and secure them in this tutorial.

Important classes/annotations used

JAX-RS provides necessary annotations to implement security in RESTEasy based application. Important annotations are:

  • javax.annotation.security.PermitAll: This annotation, when applied on an API, is used to declare that API should be accessed freely by any user. No access restrictions apply on this API.
  • javax.annotation.security.DenyAll: This annotation is used to declare that nobody is allowed to access this API irrespective of the roles assigned to any user.
  • javax.annotation.security.RolesAllowed: This annotation helps to identify that which roles are necessary for any user, to access this API. If user is validated by user/name and password but does not have role(s) specified by this annotation, he will not be allowed access.
  • javax.ws.rs.core.HttpHeaders: An interface that provides access to HTTP header information attached with a http request.
  • org.jboss.resteasy.util.Base64: This class helps in encoding and decoding to and from Base64 notations.

Apart from above classes, org.jboss.resteasy.spi.interception.PreProcessInterceptor will be used to create the security interceptor. javax.ws.rs.ext.Provider annotation will be used to register the interceptor with resteasy context.

Building the security interceptor

Before building the interceptor, lets secure the APIs. I have added @PermitAll and @RolesAllowed annotation in GET/ PUT APIs. GET API is open to all i.e. no access restriction. PUT API required a valid user with “ADMIN” capability.

@Path("/user-service")
public class UserService
{
	@PermitAll
    @GET
    @Path("/users/{id}")
    public Response getUserById(@PathParam("id") int id, @Context Request req)
    {
        Response.ResponseBuilder rb = Response.ok(UserDatabase.getUserById(id));
        return rb.build();
    }
     
	@RolesAllowed("ADMIN")
    @PUT
    @Path("/users/{id}")
    public Response updateUserById(@PathParam("id") int id)
    {
        //Update the User resource
        UserDatabase.updateUser(id);
        return Response.status(200).build();
    }
}

The security interceptor is build by implementing org.jboss.resteasy.spi.interception.PreProcessInterceptor interface. This interface has one method which implementing class class need to implement.

	public ServerResponse preProcess(HttpRequest request, ResourceMethod methodInvoked) 
															throws Failure, WebApplicationException;

methodInvoked parameter provides access to method which will be invoked as a result of calling the REST API.
request parameter provides access to request headers and parameters passed by client.

The interceptor class is defined as below:

@Provider
@ServerInterceptor
public class SecurityInterceptor implements PreProcessInterceptor
{
	//more code
}

@Provider make the resteasy context scanning to add this class as context class.
@ServerInterceptor annotation adds this class in available interceptors list.

Now, the first step is to check whether API is open for all or closed for all. This can be done by checking @PermitAll and @DenyAll annotations.

	Method method = methodInvoked.getMethod();
		
	//Access allowed for all 
	if(method.isAnnotationPresent(PermitAll.class))
	{
		return null; //Return null to continue request processing
	}
	//Access denied for all 
	if(method.isAnnotationPresent(DenyAll.class))
	{
		return ACCESS_FORBIDDEN; //Return access denied to user
	}

Next step is fetch the authorization header from request. From authorization header, we will retrieve the username and password sent by user.

	//Get request headers
	final HttpHeaders headers = request.getHttpHeaders();
	
	//Fetch authorization header
	final List<String> authorization = headers.getRequestHeader(AUTHORIZATION_PROPERTY);
	
	//If no authorization information present; block access
	if(authorization == null || authorization.isEmpty())
	{
		return ACCESS_DENIED;
	}
	
	//Get encoded username and password
	final String encodedUserPassword = authorization.get(0).replaceFirst(AUTHENTICATION_SCHEME + " ", "");
	
	//Decode username and password
	String usernameAndPassword;
	try {
		usernameAndPassword = new String(Base64.decode(encodedUserPassword));
	} catch (IOException e) {
		return SERVER_ERROR;
	}

	//Split username and password tokens
	final StringTokenizer tokenizer = new StringTokenizer(usernameAndPassword, ":");
	final String username = tokenizer.nextToken();
	final String password = tokenizer.nextToken();

Now we will get the roles necessary to access the API from @RolesAllowed annotation.

	RolesAllowed rolesAnnotation = method.getAnnotation(RolesAllowed.class);
	Set<String> rolesSet = new HashSet<String>(Arrays.asList(rolesAnnotation.value()));

rolesSet contains all the roles which can allow the API access.

Now, in final step we have to do two things. First validate the username and password from any database service, and if user is valid then get the role assigned to him. This role will be used to check the authentication success for user.

	if(rolesSet.contains(userRole))
	{
		isAllowed = true;
	}

The complete sourcecode for SecurityInterceptor.java is as given below:

/**
 * This interceptor verify the access permissions for a user 
 * based on username and passowrd provided in request
 * */
@Provider
@ServerInterceptor
public class SecurityInterceptor implements PreProcessInterceptor
{
	private static final String AUTHORIZATION_PROPERTY = "Authorization";
	private static final String AUTHENTICATION_SCHEME = "Basic";
	private static final ServerResponse ACCESS_DENIED = new ServerResponse("Access denied for this resource", 401, new Headers<Object>());;
	private static final ServerResponse ACCESS_FORBIDDEN = new ServerResponse("Nobody can access this resource", 403, new Headers<Object>());;
	private static final ServerResponse SERVER_ERROR = new ServerResponse("INTERNAL SERVER ERROR", 500, new Headers<Object>());;
	
	@Override
	public ServerResponse preProcess(HttpRequest request, ResourceMethod methodInvoked) throws Failure, WebApplicationException
	{
		Method method = methodInvoked.getMethod();
		
		//Access allowed for all 
		if(method.isAnnotationPresent(PermitAll.class))
		{
			return null;
		}
		//Access denied for all 
		if(method.isAnnotationPresent(DenyAll.class))
		{
			return ACCESS_FORBIDDEN;
		}
		
		//Get request headers
		final HttpHeaders headers = request.getHttpHeaders();
		
		//Fetch authorization header
	    final List<String> authorization = headers.getRequestHeader(AUTHORIZATION_PROPERTY);
	    
	    //If no authorization information present; block access
	    if(authorization == null || authorization.isEmpty())
	    {
	    	return ACCESS_DENIED;
	    }
	    
	    //Get encoded username and password
	    final String encodedUserPassword = authorization.get(0).replaceFirst(AUTHENTICATION_SCHEME + " ", "");
	    
	    //Decode username and password
	    String usernameAndPassword;
		try {
			usernameAndPassword = new String(Base64.decode(encodedUserPassword));
		} catch (IOException e) {
			return SERVER_ERROR;
		}

		//Split username and password tokens
	    final StringTokenizer tokenizer = new StringTokenizer(usernameAndPassword, ":");
	    final String username = tokenizer.nextToken();
	    final String password = tokenizer.nextToken();
	    
	    //Verifying Username and password
	    System.out.println(username);
	    System.out.println(password);
		
	    //Verify user access
		if(method.isAnnotationPresent(RolesAllowed.class))
		{
			RolesAllowed rolesAnnotation = method.getAnnotation(RolesAllowed.class);
			Set<String> rolesSet = new HashSet<String>(Arrays.asList(rolesAnnotation.value()));
			
			//Is user valid?
			if( ! isUserAllowed(username, password, rolesSet))
			{
				return ACCESS_DENIED;
			}
		}
		
		//Return null to continue request processing
		return null;
	}

	private boolean isUserAllowed(final String username, final String password,	final Set<String> rolesSet) 
	{
		boolean isAllowed = false;
		
		//Step 1. Fetch password from database and match with password in argument
		//If both match then get the defined role for user from database and continue; else return isAllowed [false]
		//Access the database and do this part yourself
		//String userRole = userMgr.getUserRole(username);
		String userRole = "ADMIN";
		
		//Step 2. Verify user role
		if(rolesSet.contains(userRole))
		{
			isAllowed = true;
		}
		return isAllowed;
	}
}

Testing authorization and authentication on REST APIs

To test the security code, deploy the web application in any application server like Tomcat. Now, send following requests:

HTTP GET http://localhost:8080/RESTEasyEtagDemo/user-service/users/1 without username and password

User is able to access the API successfully.

resteasy authorization test get api

HTTP PUT http://localhost:8080/RESTEasyEtagDemo/user-service/users/1 without username and password

User is not able to access the API.

resteasy authorization test get api

Add basic authorization credentials

Add basic authorization credentials

HTTP PUT http://localhost:8080/RESTEasyEtagDemo/user-service/users/1 with username and password added

User is able to access protected API

resteasy authorization test put api 2

That’s all in this tutorial. If you have any query or suggestion, drop me a comment.

Download Sourcecode

Happy Learning !!

Comments

Subscribe
Notify of
guest
31 Comments
Most Voted
Newest Oldest
Inline Feedbacks
View all comments

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.

Our Blogs

REST API Tutorial

Dark Mode

Dark Mode