In continuation to series of articles on Spring Security, here in this article we will learn ‘How to implement Security in Spring Boot using UserDetailsService With Spring Boot 3?’. After going through the previous articles, I hope we all are very familiar with basics of Security and even the basics of Security in a Spring Boot application. After the release of Spring Boot 3, here we are going to implement ‘Spring Security UserDetailsService Using Spring Boot 3’.
In this article, we will create a user registration form and save users with their roles in the database. Then, based on the user role, we will check the authentication and authorization functionalities with the help of predefined UserDetailsService.
To illustrate, we will take some roles into effect and play around them in the whole process to make it crystal clear. Additionally, we will have some pages and restrict them to be accessible by some specific roles only. In order to make it possible, we will create a small, simple MVC web application and make registration process open for all users. Users will select their respective roles while filling registration form. Then, we will implement security features on top of it. Let’s start working on our topic ‘Spring Security UserDetailsService Using Spring Boot 3’ accordingly.
Table of Contents (Click on links below to navigate)
- 1 What can you expect from this article as a whole?
- 2 Software/Technologies Used in the Example
- 3 What is UserDetailsService all about? What is the benefit of using it?
- 4 How to incorporate UserDetailsService Security in our application?
- 5 How does we implement Role Based Security in a Spring Based Application?
- 6 Example of Spring Security UserDetailsService Using Spring Boot 3
- 6.1 Use case Details
- 6.2 Step#1: Create a Spring Boot Starter Project in STS(Spring Tool Suite)
- 6.3 Step#2: Update database properties in application.properties file
- 6.4 Step#3: Create User Entity & Repository classes
- 6.5 Step#4: Create AppConfig class to instantiate BCryptPasswordEncoder
- 6.6 Step#5: Create Service Interface & Service Implementation class
- 6.7 Step#6: Create a UserController class
- 6.8 Step#7: Write a Controller class to navigate through pages
- 6.9 Step#8: Write UI pages(Thymeleaf)
- 6.10 Step#9: Write SecurityConfig class Without Using WebSecurityConfigurerAdapter
- 7 How to test the security enabled Application ?
- 8 How To Migrate your previous implementation to Spring Security UserDetailsService Using Spring Boot 3?
- 9 Summary
What can you expect from this article as a whole?
1) What is a UserDetailsService concept in the context of Spring Security?
2) What is the benefits of implementing UserDetailsService?
3) How to implement Spring Security UserDetailsService Using Spring Boot 3?
4) How to Implement Role Based Security in a Spring Based application?
5) Moreover, how and where to use annotations :@EnableWebSecurity, @Configuration, @Bean, @GetMapping, @Autowired, @Data, @Entity, @Table, @Id, @GeneratedValue, @Column, @ElementCollection, @CollectionTable, @JoinColumn, @Service
6) How to develop a user registration app using Spring MVC with Thymeleaf?
7) How to test the Security enabled application?
8) How to implement Security in Spring Boot using UserDetailsService Without WebSecurityConfigurerAdapter?
Software/Technologies Used in the Example
Sometimes some version conflicts with other version. Hence, listing down the combinations that are tested to be working with each other. Below is the tested combination of software that are used to develop Spring Security UserDetailsService Using Spring Boot 3. It also makes the implementation flawless.
1) Spring Boot 3.0.0
2) JDK 17 or later
3) Maven 3.8.1
4) IDE – STS 4.7.1. RELEASE
Below are the list of major jars automatically downloaded by maven using pom.xml in these examples. They might be useful to cross verify in case if you face any issue in execution.
What is UserDetailsService all about? What is the benefit of using it?
UserDetailsService is a predefined interface provided by Spring framework under the package org.springframework.security.core.userdetails. In order to make use of UserDetailsService, our implementation class implements this interface and overrides it’s loadUserByUsername(String username) method. The return type of this method is UserDetails which is again an interface. Predefined User class (org.springframework.security.core.userdetails.User) is an implementation of UserDetails interface. Furthermore, we pass our username in loadUserByUsername(String username) method, and it returns us predefined User object(org.springframework.security.core.userdetails.User).
In fact, we provide only username to UserDetailsService and some small configurations. As a result, we get all role based in-built security functionality implemented as part of the framework. Accordingly, we save a lot of effort in implementing security when we use UserDetailsService interface.
How to incorporate UserDetailsService Security in our application?
First of all, you must have a Spring Boot web application where in you will have a form i.e. a kind of User registration form. As part of the Spring MVC structure you will have a UserService implementation class. Let’s say it UserServiceImpl.java. The second thing to remember is that you have to convert your User object into predefined Spring’s User object. Further, please follow below steps to get UserDetailsService implemented in your application.
1) Your user service class ‘UserServiceImpl.java’ should implement interface UserDetailsService.java(Provided by Spring)
2) Equally important, Override loadUserByUsername(String username) method of interface UserDetailsService in your UserServiceImpl class.
3) As part of implementation,
(A) Get your User Object with the help of username/email from UserRepository.
(B) Convert your User Object into Spring’s predefined User object(org.springframework.security.core.userdetails.User) accordingly.
(C) Return Spring defined User object which is an implementation of UserDetails(method’s return type).
Below code represents the implementation of UserDetailsService. However, you will see the complete code in below sections consequently.
How does we implement Role Based Security in a Spring Based Application?
Typically, in a Spring Based Application, we implement Role Based Access by creating a java class and applying @EnableWebSecurity and @Configuration on top of it. @EnableWebSecurity enables Spring Security features in the application, whereas @Configuration represents that this class is a configuration class. For example, below code demonstrates the implementation of a role based security.
Example of Spring Security UserDetailsService Using Spring Boot 3
In order to simplify the implementation of Spring Security UserDetailsService Using Spring Boot 3, let’s consider a use case.
Use case Details
Let’s assume an internal portal of a small organization. In the organization, we have various roles within employees such as Admin, HR, Manager and of course Employee as well. Additionally, the Portal has role based access to pages. Furthermore, some pages should be accessible to all roles such as registration and the public info pages, while others should be restricted to their respective roles only.
Needless to say, the organization will have a user registration page which must be accessible to all users, even without login. Now let’s create a standard user registration flow as given below.
Step#1: Create a Spring Boot Starter Project in STS(Spring Tool Suite)
While creating Starter Project select ‘Spring Security’, ‘Thymeleaf’, ‘Spring Web’, ‘Spring Data JPA’, ‘MySQL Driver’, ‘Lombok’ and ‘Spring Boot DevTools’ as starter project dependencies. Even If you don’t know how to create a Spring Boot Starter Project, Kindly visit internal link on ‘How to create a starter project in Spring boot?‘. Also, if you want to know more about Lombok, then visit Internal Link on Lombok.
Step#2: Update database properties in application.properties file
Update application.properties to connect to MySQL Database. Please note that we can also omit driver-class-name as Spring Boot will automatically find it from the database URL as shown below. However, it is recommended to keep it.
#application.properties --------------------------------------------------------------------- #-------------------- server properties --------------- server.port=8080 #--------------------- DB Connection ------------------ #AutoLoading of driver class since JDBC 4 #spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/testBootSecurity spring.datasource.username=root spring.datasource.password=devs #--------------------JPA-ORM Properties----------------- spring.jpa.show-sql=true spring.jpa.hibernate.ddl-auto=update #spring.jpa.database-platform=org.hibernet.dialect.MySQL8Dialect
Step#5: Create Service Interface & Service Implementation class
Create Service Interface and Service Impl class as IUserService.java and UserServiceImpl.java accordingly as shown below. In fact, implementation of loadUserByUsername(String email) method in UserServiceImpl.java is the most important part of your UserDetailsService on the whole.
In spite of UserController, write one more controller class and call it as ‘HomeController.java’. This class will be responsible to navigate through different pages.
Step#8: Write UI pages(Thymeleaf)
Below are the .html files for UI pages. Place these pages inside ‘src/main/resources/templates’ folder accordingly.
Step#9: Write SecurityConfig class Without Using WebSecurityConfigurerAdapter
At the end, write the other important class as SecurityConfig.java. Prior to Spring Security 5.7.0-M2, this class was supposed to extend a predefined class WebSecurityConfigurerAdapter.java and implement two configure() methods accordingly. But since Spring Security 5.7.0-M2, the WebSecurityConfigurerAdapter has been deprecated. Furthermore, since Spring Boot 3.0.0 and Spring Security 6.0, the WebSecurityConfigurerAdapter is completely removed from the Spring Security API.
Hence, the required implementation is applicable since Spring 3.0.0 as shown in below code snippet.
From the above implementation of SecurityConfig, it is clear that some of the methods from older versions are also removed. For example:
authorizeRequests() -> authorizeHttpRequests()
antMatchers() -> requestMatchers()
regexMatchers() -> RegexRequestMatchers()
Finally, we have completed the coding part.
How to test the security enabled Application ?
Although the word ‘testing’ looks very easy to a developer, but it is equally important as it provides the result of our implementation on the whole. While testing the application you should keep configure(HttpSecurity http) method of SecurityConfig class in front of you, then follow the steps given below:
1) Start the application : Right click on the project, then select “Run As’ >> ‘Spring Boot App’.
2) Enter the registration page URL http://localhost:8080/register, then check if it is accessible to everyone without even login to the application.
3) Enter the required field values and complete the registration process by clicking on the ‘Register’ button accordingly.
4) Now Enter any URL specific to the role you selected in registration. Suppose you entered URL http://localhost:8080/admin, then it should redirect you to the in-built Login page.
5) Enter the credentials(email id in place of username) and Login to the application. It will redirect you to the default success URL which is welcome page.
6) Now enter the URL http://localhost:8080/admin again, this time you will be able to access the admin page.
7) Repeat the above steps for other roles as well.
Additionally, as mentioned above keep the SecurityConfig.java code in front of you and test each scenario subsequently.
How To Migrate your previous implementation to Spring Security UserDetailsService Using Spring Boot 3?
Here are some step by step guidelines, that you can follow to migrate from older version implementation to Spring Security UserDetailsService Using Spring Boot 3.
1) As recommended by Spring Official documentation, first upgrade your implementation to Spring Boot 2.7.0 if it is implemented using lower versions. We can do it by updating Spring Boot version in pom.xml as below.
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.0</version> <relativePath/> <!-- lookup parent from repository --> </parent>
Once updated, save the pom.xml and let the maven download the new dependencies.
2) For this example, you will find the WebSecurityConfigurerAdapter as deprecated. Provide the new implementation without using WebSecurityConfigurerAdapter. You may use the implementation of SecurityConfig.java from this article. If there are some other errors, fix them as well.
3) In the next step, configure JDK 17 environment in your project as recommended in Spring Boot Official documentation.
4) Update the Spring Boot version as ‘3.0.0’ in pom.xml, save the file and let the maven download the new dependencies.
5) Fix the compilation errors as shown below:
(A) In SecurityConfig.java:
Replace authorizeRequests() with authorizeHttpRequests()
Replace antMatchers() with requestMatchers()
Replace regexMatchers() with RegexRequestMatchers()
(B) In Entity class:
In import statements, replace all occurrences of ‘javax’ with ‘jakarta’. For example: ‘javax.persistence.Entity;’ should be replaced with ‘jakarta.persistence.Entity;’.
While upgrading Spring Boot version to 3.0.0, you may face below error:
[ERROR] Some problems were encountered while processing the POMs: [ERROR] 'dependencies.dependency.version' for org.thymeleaf.extras:thymeleaf-extras-springsecurity5:jar is missing.