Adding Role Based Security With Spring Boot REST APIs
Adding Role Based Security With Spring Boot REST APIs
Learn to create JAX-RS 2.0 REST APIs using Spring Boot and Jersey framework, and add
role based security using JAX-RS annotations e.g. @PermitAll, @RolesAllowed or
@DenyAll.
Table of Contents
Project Structure
Create REST APIs
Secure REST APIs with JAX-RS Annotations
Write security filter using JAX-RS ContainerRequestFilter
Demo
Project Structure
Current Time 0:00
/
Duration 1:45
Go to Spring Initializr portal and create spring boot application with Jersey (JAX-
RS) dependency.
2. Import in Eclipse
Generate the project as zip file. Extract it in some place in your computer. Import the
project as ‘Existing maven application’ into eclipse.
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jersey</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
Now create some JAX-RS resources which we will access into testing phase. I have
created UserResource class.
UserResource.java
package com.howtodoinjava.jerseydemo;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Response;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement(name = "users")
@Path("/users")
@GET
@Produces("application/json")
users.setUsers(new ArrayList<>(DB.values()));
return users;
}
@POST
@Consumes("application/json")
{
}
user.setId(DB.values().size()+1);
user.setUri("/user-management/"+user.getId());
DB.put(user.getId(), user);
return Response.status(201).contentLocation(new
URI(user.getUri())).build();
}
@GET
@Path("/{id}")
@Produces("application/json")
{
if(user == null) {
return Response.status(404).build();
}
return Response
.status(200)
.entity(user)
.contentLocation(new URI("/user-management/"+id)).build();
}
@PUT
@Path("/{id}")
@Consumes("application/json")
@Produces("application/json")
{
if(user == null) {
return Response.status(404).build();
}
temp.setFirstName(user.getFirstName());
temp.setLastName(user.getLastName());
DB.put(temp.getId(), temp);
return Response.status(200).entity(temp).build();
}
@DELETE
@Path("/{id}")
if(user != null) {
DB.remove(user.getId());
return Response.status(200).build();
}
return Response.status(404).build();
}
static
{
user1.setId(1);
user1.setFirstName("John");
user1.setLastName("Wick");
user1.setUri("/user-management/1");
User user2 = new User();
user2.setId(2);
user2.setFirstName("Harry");
user2.setLastName("Potter");
user2.setUri("/user-management/2");
DB.put(user1.getId(), user1);
DB.put(user2.getId(), user2);
}
Users.java
package com.howtodoinjava.jerseydemo;
import java.util.ArrayList;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement(name = "users")
@XmlElement(name="user")
return users;
}
this.users = users;
}
User.java
package com.howtodoinjava.jerseydemo;
import java.io.Serializable;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement(name = "user")
@XmlAttribute(name = "id")
@XmlAttribute(name="uri")
@XmlElement(name = "firstName")
private String firstName;
@XmlElement(name = "lastName")
5. Configure Jersey
Now we have a JAX-RS resource and we want to access it from spring boot
application which include Jersey dependency. Let’s register this resource as Jersey
resource.
package com.howtodoinjava.jerseydemo;
import org.glassfish.jersey.server.ResourceConfig;
import org.springframework.stereotype.Component;
@Component
public JerseyConfig()
{
register(SecurityFilter.class);
register(UserResource.class);
}
package com.howtodoinjava.jerseydemo;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import
org.springframework.boot.web.support.SpringBootServletInitializer;
@SpringBootApplication
{
new JerseydemoApplication().configure(new
SpringApplicationBuilder(JerseydemoApplication.class)).run(args);
}
Now when our APIs are ready, we will start securing them. Let’s annotate the APIs with
JAX-RS annotations based on their desired access level and user roles allowed to access
them.
package com.howtodoinjava.jerseydemo;
@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement(name = "users")
@Path("/users")
@GET
@PermitAll
@Produces("application/json")
return users;
}
@POST
@Consumes("application/json")
@RolesAllowed("ADMIN")
{
}
user.setId(DB.values().size()+1);
user.setUri("/user-management/"+user.getId());
DB.put(user.getId(), user);
return Response.status(201).contentLocation(new
URI(user.getUri())).build();
}
@GET
@Path("/{id}")
@Produces("application/json")
@PermitAll
{
if(user == null) {
return Response.status(404).build();
}
return Response
.status(200)
.entity(user)
.contentLocation(new URI("/user-management/"+id)).build();
}
@PUT
@Path("/{id}")
@Consumes("application/json")
@Produces("application/json")
@RolesAllowed("ADMIN")
{
if(user == null) {
return Response.status(404).build();
}
temp.setFirstName(user.getFirstName());
temp.setLastName(user.getLastName());
DB.put(temp.getId(), temp);
return Response.status(200).entity(temp).build();
}
@DELETE
@Path("/{id}")
@RolesAllowed("ADMIN")
if(user != null) {
DB.remove(user.getId());
return Response.status(200).build();
}
return Response.status(404).build();
}
static
{
user1.setId(1);
user1.setFirstName("John");
user1.setLastName("Wick");
user1.setUri("/user-management/1");
User user2 = new User();
user2.setId(2);
user2.setFirstName("Harry");
user2.setLastName("Potter");
user2.setUri("/user-management/2");
DB.put(user1.getId(), user1);
DB.put(user2.getId(), user2);
}
You can see the security related JAX-RS annotations in above highlighted lines.
Now it’s time to write our security filter which will examine the incoming requests, fetch the
authorization information (basic auth in this example), and then will match user name and
password, and finally it will verify the user’s access level by it’s role. If everything matches,
API will be accessed else user will get access denied response.
package com.howtodoinjava.jerseydemo;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Base64;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
import javax.annotation.security.DenyAll;
import javax.annotation.security.PermitAll;
import javax.annotation.security.RolesAllowed;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ResourceInfo;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.Provider;
/**
* */
@Provider
@Context
@Override
{
if( ! method.isAnnotationPresent(PermitAll.class))
{
if(method.isAnnotationPresent(DenyAll.class))
{
requestContext.abortWith(ACCESS_FORBIDDEN);
return;
}
{
requestContext.abortWith(ACCESS_DENIED);
return;
}
try {
usernameAndPassword = new
String(Base64.getDecoder().decode(encodedUserPassword));
return;
}
if(!(username.equalsIgnoreCase("admin") &&
password.equalsIgnoreCase("password"))){
requestContext.abortWith(ACCESS_DENIED);
return;
}
if(method.isAnnotationPresent(RolesAllowed.class))
{
RolesAllowed rolesAnnotation =
method.getAnnotation(RolesAllowed.class);
{
requestContext.abortWith(ACCESS_DENIED);
return;
}
}
}
}
{
//If both match then get the defined role for user from database
and continue; else return isAllowed [false]
if(rolesSet.contains(userRole))
{
isAllowed = true;
}
return isAllowed;
}
Demo
Run the project as Spring boot application. Now test rest resources.
Use this link to generate base64 encoded user name and password combination to pass into
Authorization header.
Request with Auth Details Success
Happy Learning !!