This tutorial is the suite of the following tutorial where we implemented a JWT authentication in Spring Boot 3 and Spring Security 6.
We have a Web API that has public routes and restricted routes. The restricted routes require a valid JWT from a user in the database.
Now that the user is authenticated, we want to go further by allowing access to some data only if the user has a specific role.
Here are the following roles we have in our system:
- User: can access his information
- Administrator: can do everything the User role does and access the users’ list.
- Super Administrator: can do everything the Admin role does and create an admin user; shortly, he can do everything. Below are the list of protected routes with the roles required to access them.
Route: [GET] /users/me Roles: User, Admin, Super Admin Description: retrieve the authenticated user.
Route: [GET] /users Roles: Admin, Super Admin Description: retrieve the list of all users.
Route: [POST] /admins Roles: Super Admin Description: Create an administrator.
- Start by running postgres docker image with environment variables and port mapping application.yml most have the same environment variable and port than docker db
docker run -d -e POSTGRES_PASSWORD=secret -e POSTGRES_DB=springbootsecuritydb -e POSTGRES_USER=pw --name postgresdb -p 5433:5432 postgres
- To connect from host according to environment variable
psql -p $LOCAL_PORT -h 127.0.0.1 -d $YOUR_DB_NAME -U $YOUR_USERNAME -W
psql -p 5433 -h 127.0.0.1 -d springbootsecuritydb -U pw -W- start spring boot application it will create needed table on db by schema.sql
curl -XPOST -H "Content-type: application/json" -d '{
"email": "jon@snow.com",
"password": "123456",
"fullName": "Jon Snow"
}' 'http://localhost:8085/auth/signup'
The role entity will represent the different roles needed in our system
3- Store the pre-defined roles in the database (in my case i have use script data.sql, but below there is another ways to do in spring boot)
We already know roles to persist in the system. So, before creating a user, we must ensure the role exists in the database.
We will create a function executed at the application startup to create roles in the database if they don’t exist.
Spring Boot allows executing some actions on the application startup; we will use it here, so let’s create a package called “bootstrap”, then create the file “RoleSeeder.java” and add the code below:
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
import java.util.*;
@Component
public class RoleSeeder implements ApplicationListener<ContextRefreshedEvent> {
private final RoleRepository roleRepository;
public RoleSeeder(RoleRepository roleRepository) {
this.roleRepository = roleRepository;
}
@Override
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
this.loadRoles();
}
private void loadRoles() {
RoleEnum[] roleNames = new RoleEnum[]{RoleEnum.USER, RoleEnum.ADMIN, RoleEnum.SUPER_ADMIN};
Map<RoleEnum, String> roleDescriptionMap = Map.of(
RoleEnum.USER, "Default user role",
RoleEnum.ADMIN, "Administrator role",
RoleEnum.SUPER_ADMIN, "Super Administrator role"
);
Arrays.stream(roleNames).forEach((roleName) -> {
Optional<Role> optionalRole = roleRepository.findByName(roleName);
optionalRole.ifPresentOrElse(System.out::println, () -> {
Role roleToCreate = new Role();
roleToCreate.setName(roleName)
.setDescription(roleDescriptionMap.get(roleName));
roleRepository.save(roleToCreate);
});
});
}
}Re-run the application and verify the roles have been created in the database.
@OneToOne(cascade = CascadeType.REMOVE)
@JoinColumn(name = "role_id", referencedColumnName = "id", nullable = false)
private Role role;
public Role getRole(){
return role;
}
public User setRole(Role role){
this.role=role;
return this;
}The user role is now required, so creating a user without one will throw an error. We must update signup() function in the AuthenticationService.java;
curl -XPOST -H "Content-type: application/json" -d '{
"email": "jones@sill.com",
"password": "98765",
"fullName": "Jones Sill"
}' 'http://localhost:8085/auth/signup'
In the user entity class (User.java), the function getAuthorities() returns all the authorities associated with this user; it was empty by default, but now we must update it to produce a list containing the user's role name.
To restrict user access based on their roles, we must enable the feature in Spring security, allowing us to perform the check without writing a custom logic. You must add the annotation @EnableMethodSecurity on the security configuration file “SecurityConfiguration.java”.
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class SecurityConfiguration {
// The existing code configuration here...
}Adding this annotation gives us annotations we can use at the controller level to perform role access control.
Open the file UserController.java and add the annotation for the following routes:
- “/users/me”: @PreAuthorize(“isAuthenticated()”)
- “/users”: @PreAuthorize(“hasAnyRole(‘ADMIN’, ‘SUPER_ADMIN’)”) Since the route “users/me” is accessible by all the roles, we can just check that the user is authenticated.
The endpoint “/admins” creates a new admin in the system and is accessible only by a user having a super admin role.
Since the implementation doesn’t exist, let’s add it in the UserService.java
There is no endpoint to create a super admin, so we must create one at the application startup. In the package “bootstrap”, create a file “AdminSeeder.java”
In this post, we saw how to implement a Role Based Access Control in a Spring Boot application, and here are the main steps to remember:
Create the role entity and data access layer. Associate the user entity with a role. Expose the user’s role in the authentication context. Enable the method security Spring security. Protect the API route using the method security route isAuthenticated(), hasRole() and hasAnyRole().


