- Spring Boot Security Role-based Authorization Tutorial
- 1. Design and Create Tables
- 2. Configure Data Source Properties
- 3. Configure Dependencies
- 4. Code Entity Classes
- 5. Code UserRepository
- 6. Implement UserDetails and UserDetaisService
- 7. Configure Spring Security Authentication & Authorization
- 8. Implement Authorization using Thymeleaf Integration with Spring Security
- Other Spring Security Tutorials:
- Other Spring Boot Tutorials:
- About the Author:
Spring Boot Security Role-based Authorization Tutorial
In this tutorial, I will guide you how to use Spring Security to authorize users based on their roles for a Spring Boot application. The credentials and roles are stored dynamically in MySQL database. Spring Data JPA with Hibernate is used for the data access layer and Thymeleaf integration with Spring Security is used for the view layer.
We will write code to secure an existing Spring Boot project Product Manager which is described in this tutorial. So I recommend you to download that project in order to follow this tutorial easily. For authorization, we will create some users with different roles (authorities) as follows:
The role USER allows user to view all products; the role CREATOR is permission to create new products; the role EDITOR is for editing products; and the role ADMIN gives all permissions to the users.
1. Design and Create Tables
For role-based authorization with credentials and authorities stored in database, we have to create the following 3 tables:
The users table stores credentials and the roles table stores authorities (rights). The entity relationship between users and roles is many-to-many because a user can have one or more roles and a role can be assigned to be one or more users. That’s why we need to have the intermediate table users_roles to realize that many-to-many association.
You can execute the following MySQL script to create these tables:
CREATE TABLE `users` ( `user_id` int(11) NOT NULL AUTO_INCREMENT, `email` varchar(45) NOT NULL, `full_name` varchar(45) NOT NULL, `password` varchar(64) NOT NULL, `enabled` tinyint(4) DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `email_UNIQUE` (`email`) ); CREATE TABLE `roles` ( `role_id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(45) NOT NULL, PRIMARY KEY (`id`) ); CREATE TABLE `users_roles` ( `user_id` int(11) NOT NULL, `role_id` int(11) NOT NULL, KEY `user_fk_idx` (`user_id`), KEY `role_fk_idx` (`role_id`), CONSTRAINT `role_fk` FOREIGN KEY (`role_id`) REFERENCES `roles` (`role_id`), CONSTRAINT `user_fk` FOREIGN KEY (`user_id`) REFERENCES `users` (`user_id`) );
INSERT INTO `roles` (`name`) VALUES ('USER'); INSERT INTO `roles` (`name`) VALUES ('CREATOR'); INSERT INTO `roles` (`name`) VALUES ('EDITOR'); INSERT INTO `roles` (`name`) VALUES ('ADMIN');
INSERT INTO `users` (`username`, `password`, `enabled`) VALUES ('patrick', '$2a$10$cTUErxQqYVyU2qmQGIktpup5chLEdhD2zpzNEyYqmxrHHJbSNDOG.', '1'); INSERT INTO `users` (`username`, `password`, `enabled`) VALUES ('alex', '$2a$10$.tP2OH3dEG0zms7vek4ated5AiQ.EGkncii0OpCcGq4bckS9NOULu', '1'); INSERT INTO `users` (`username`, `password`, `enabled`) VALUES ('john', '$2a$10$E2UPv7arXmp3q0LzVzCBNeb4B4AtbTAGjkefVDnSztOwE7Gix6kea', '1'); INSERT INTO `users` (`username`, `password`, `enabled`) VALUES ('namhm', '$2a$10$GQT8bfLMaLYwlyUysnGwDu6HMB5G.tin5MKT/uduv2Nez0.DmhnOq', '1'); INSERT INTO `users` (`username`, `password`, `enabled`) VALUES ('admin', '$2a$10$IqTJTjn39IU5.7sSCDQxzu3xug6z/LPU6IF0azE/8CkHCwYEnwBX.', '1');
And execute the following script to assign permissions to users based on the table above:
INSERT INTO `users_roles` (`user_id`, `role_id`) VALUES (1, 1); -- user patrick has role USER INSERT INTO `users_roles` (`user_id`, `role_id`) VALUES (2, 2); -- user alex has role CREATOR INSERT INTO `users_roles` (`user_id`, `role_id`) VALUES (3, 3); -- user john has role EDITOR INSERT INTO `users_roles` (`user_id`, `role_id`) VALUES (4, 2); -- user namhm has role CREATOR INSERT INTO `users_roles` (`user_id`, `role_id`) VALUES (4, 3); -- user namhm has role EDITOR INSERT INTO `users_roles` (`user_id`, `role_id`) VALUES (5, 4); -- user admin has role ADMIN
2. Configure Data Source Properties
To use Spring Boot with Spring Data JPA and Hibernate, configure database connection information in the application.properties as follows:
spring.jpa.hibernate.ddl-auto=none spring.datasource.url=jdbc:mysql://localhost:3306/sales spring.datasource.username=root spring.datasource.password=password
3. Configure Dependencies
Make sure that the Maven build file includes the following dependency declaration for Spring Web, Spring Data JPA, Spring Security, Thymeleaf, MySQL JDBC driver and Thymeleaf extras for Spring Security:
org.springframework.boot spring-boot-starter-data-jpa org.springframework.boot spring-boot-starter-security org.springframework.boot spring-boot-starter-thymeleaf org.springframework.boot spring-boot-starter-web mysql mysql-connector-java runtime org.thymeleaf.extras thymeleaf-extras-springsecurity5
Note that by default, if the Spring Security library present in the classpath, the users must login to use the application.
4. Code Entity Classes
Next, we need to create two entity classes that map with the tables users and roles in the database. The first class is Role :
package net.codejava; import javax.persistence.*; @Entity @Table(name = "roles") public class Role < @Id @Column(name = "role_id") @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; private String name; public Integer getId() < return id; >// remaining getters and setters >
package net.codejava; import java.util.*; import javax.persistence.*; @Entity @Table(name = "users") public class User < @Id @Column(name = "user_id") @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String username; private String password; private boolean enabled; @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER) @JoinTable( name = "users_roles", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "role_id") ) private Setroles = new HashSet<>(); public Long getId() < return id; >// remaining getters and setters are not shown for brevity >
Here, you can see we use a Set of Roles in the User class to map a unidirectional many-to-many association from User to Role , e.g. user.roles . Refer to this tutorial for details about Hibernate many-to-many relationship mapping.
5. Code UserRepository
package net.codejava; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.query.Param; public interface UserRepository extends CrudRepository
This interface is a subtype of CrudRepository defined by Spring Data JPA so Spring will generate implementation class at runtime. We define the getUserByUsername() method annotated by a JPA query to be used by Spring Security for authentication. If you’re new to Spring Data JPA, kind check this quick start guide.
6. Implement UserDetails and UserDetaisService
Spring Security requires an implementation of UserDetails interface to know about the authenticated user information, so we create the MyUserDetails class as follows:
package net.codejava; import java.util.*; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; public class MyUserDetails implements UserDetails < private User user; public MyUserDetails(User user) < this.user = user; >@Override public Collection extends GrantedAuthority>getAuthorities() < Setroles = user.getRoles(); List authorities = new ArrayList<>(); for (Role role : roles) < authorities.add(new SimpleGrantedAuthority(role.getName())); >return authorities; > @Override public String getPassword() < return user.getPassword(); >@Override public String getUsername() < return user.getUsername(); >@Override public boolean isAccountNonExpired() < return true; >@Override public boolean isAccountNonLocked() < return true; >@Override public boolean isCredentialsNonExpired() < return true; >@Override public boolean isEnabled() < return user.isEnabled(); >>
You can see, this class wraps an instance of User class and delegates almost overriding methods to the User ’s ones. For authorization, pay attention to this method:
@Override public Collection extends GrantedAuthority>getAuthorities() < Setroles = user.getRoles(); List authorities = new ArrayList<>(); for (Role role : roles) < authorities.add(new SimpleGrantedAuthority(role.getName())); >return authorities; >
This method returns a set of roles (authorities) to be used by Spring Security in the authorization process.
Next, we need to code an implementation of the UserDetailsService interface defined by Spring Security with the following code:
package net.codejava; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.userdetails.*; public class UserDetailsServiceImpl implements UserDetailsService < @Autowired private UserRepository userRepository; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException < User user = userRepository.getUserByUsername(username); if (user == null) < throw new UsernameNotFoundException("Could not find user"); >return new MyUserDetails(user); > >
As you can see, this class makes use an instance of UserRepository interface in the loadUserByUsername() method which will be invoked by Spring Security when authenticating the users.
7. Configure Spring Security Authentication & Authorization
And to connect all the pieces together, we code a Spring Security configuration class with the following code:
package net.codejava; import org.springframework.context.annotation.*; import org.springframework.security.authentication.dao.*; import org.springframework.security.config.annotation.authentication.builders.*; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.*; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; @Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter < @Bean public UserDetailsService userDetailsService() < return new UserDetailsServiceImpl(); >@Bean public BCryptPasswordEncoder passwordEncoder() < return new BCryptPasswordEncoder(); >@Bean public DaoAuthenticationProvider authenticationProvider() < DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider(); authProvider.setUserDetailsService(userDetailsService()); authProvider.setPasswordEncoder(passwordEncoder()); return authProvider; >@Override protected void configure(AuthenticationManagerBuilder auth) throws Exception < auth.authenticationProvider(authenticationProvider()); >@Override protected void configure(HttpSecurity http) throws Exception < http.authorizeRequests() .antMatchers("/").hasAnyAuthority("USER", "CREATOR", "EDITOR", "ADMIN") .antMatchers("/new").hasAnyAuthority("ADMIN", "CREATOR") .antMatchers("/edit/**").hasAnyAuthority("ADMIN", "EDITOR") .antMatchers("/delete/**").hasAuthority("ADMIN") .anyRequest().authenticated() .and() .formLogin().permitAll() .and() .logout().permitAll() .and() .exceptionHandling().accessDeniedPage("/403") ; >>
The first 4 methods are needed to configure an authentication provider that uses Spring Data JPA and Hibernate. And in the last method we configure HTTP Security for authentication and authorization. We also configure a custom URL for displaying access denied error in case the users do not have permission.
8. Implement Authorization using Thymeleaf Integration with Spring Security
To use Thymeleaf with Spring Security for the view, make sure you declare the relevant XML namespaces like this:
Welcome Username Roles
Because only the users have role CREATOR or ADMIN can create new products, so write the following code to show the Create New Product link which is visible to only authorized users:
The users with role EDITOR or ADMIN can see the links to edit/update products, thus the following code:
I recommend you to watch the following video to see the coding in action and how I test the login, logout and authorization:
So far you have learned how to authorize users based on their roles using Spring Security and Thymeleaf for a Spring Boot Application. You see, Spring framework makes it easy and convenient to implement authorization with minimal effort. For reference, you can download the sample project attached below.
You can also the the sample project on GitHub here.
Other Spring Security Tutorials:
Other Spring Boot Tutorials:
About the Author:
Nam Ha Minh is certified Java programmer (SCJP and SCWCD). He started programming with Java in the time of Java 1.4 and has been falling in love with Java since then. Make friend with him on Facebook and watch his Java videos you YouTube.