You are here
Home > java >

Spring Security UserDetailsService Using Spring Boot 3

Spring Security UserDetailsService Using Spring Boot 3In 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.

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

Jars Used

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.

1) spring-boot-3.0.0.jar
2) spring-boot-starter-3.0.0.jar
3) spring-boot-starter-security-3.0.0.jar
4) spring-core-6.0.2.jar
5) spring-security-core-6.0.0.jar
6) thymeleaf-spring6-3.1.0.RELEASE.jar

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.

UserServiceImpl.java

import java.util.Optional;
import java.util.stream.Collectors;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import com.dev.springboot.security.UserDetailsService.model.User;
import com.dev.springboot.security.UserDetailsService.repo.UserRepository;
import com.dev.springboot.security.UserDetailsService.service.IUserService;

@Service
public class UserServiceImpl implements IUserService, UserDetailsService{

@Autowired
private UserRepository userRepo;

@Autowired
private BCryptPasswordEncoder passwordEncoder;

@Override
public Integer saveUser(User user) {
String passwd= user.getPassword();
String encodedPasswod = passwordEncoder.encode(passwd);
user.setPassword(encodedPasswod);
user = userRepo.save(user);
return user.getId();
}

@Override
public UserDetails loadUserByUsername(String email)
throws UsernameNotFoundException {

Optional<User> opt = userRepo.findUserByEmail(email);

if(opt.isEmpty())
throw new UsernameNotFoundException("User with email: " +email +" not found !");
else {
User user = opt.get();
return new org.springframework.security.core.userdetails.User(
user.getEmail(),
user.getPassword(),
user.getRoles()
.stream()
.map(role-> new SimpleGrantedAuthority(role))
.collect(Collectors.toSet())
);
}

}

//Other Approach: Without Using Lambda & Stream API Of Java 8

/** @Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {

Optional<User> opt = userRepo.findUserByEmail(email);

org.springframework.security.core.userdetails.User springUser=null;

if(opt.isEmpty()) {
throw new UsernameNotFoundException("User with email: " +email +" not found");
}
User user =opt.get();
List<String> roles = user.getRoles();
Set<GrantedAuthority> ga = new HashSet<>();
for(String role:roles) {
ga.add(new SimpleGrantedAuthority(role));
}

springUser = new org.springframework.security.core.userdetails.User(
email,
user.getPassword(),
ga );
return springUser;
} */

}

How can 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.

SecurityConfig.java
package com.dev.springboot.security.UserDetailsService.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

@EnableWebSecurity
@Configuration
public class SecurityConfig {

@Autowired
private UserDetailsService uds;

@Autowired
private BCryptPasswordEncoder encoder;

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {

http.authorizeRequests()
.antMatchers("/home","/register","/saveUser").permitAll()
.antMatchers("/welcome").authenticated()
.antMatchers("/admin").hasAuthority("Admin")
.antMatchers("/mgr").hasAuthority("Manager")
.antMatchers("/emp").hasAuthority("Employee")
.antMatchers("/hr").hasAuthority("HR")
.antMatchers("/common").hasAnyAuthority("Employeee,Manager,Admin")
.anyRequest().authenticated()

.and()
.formLogin()
.defaultSuccessUrl("/welcome",true)

.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))

.and()
.exceptionHandling()
.accessDeniedPage("/accessDenied")

.and()
.authenticationProvider(authenticationProvider());

return http.build();

}

@Bean
public AuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setUserDetailsService(uds);
authenticationProvider.setPasswordEncoder(encoder);
return authenticationProvider;
}
}

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.

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.

Spring Security UserDetailsService Using Spring Boot 3

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#3: Create User Entity & Repository classes

Now create User.java & UserRepositoty.java as below. Please note that Spring Boot 3.0.0 and Spring Security 6.0 onward, all the import statements starting with โ€˜javaxโ€™ are replaced with โ€˜jakartaโ€™ as shown in the code below. For example: โ€˜javax.persistence.Entity;โ€™ should be replaced with โ€˜jakarta.persistence.Entity;โ€™.

Equally important, User.java has a variable โ€˜rolesโ€™ of type List<String>. It will create a separate table in the database with two columns user_id and user_role consequently. Further, @ElementCollection(fetch= FetchType.EAGER) indicates that while fetching User object, also fetch roles simultaneously. On the other hand, UserRepository extends โ€˜JpaRepositoryโ€™ to take advantage of inbuilt database operations.

User.java
package com.dev.springboot.security.UserDetailsService.model;

import java.util.List;

import jakarta.persistence.CollectionTable;
import jakarta.persistence.Column;
import jakarta.persistence.ElementCollection;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.Table;

import lombok.Data;

@Data
@Entity
@Table(name="users")
public class User {

@Id
@GeneratedValue
@Column(name="user_id")
private Integer id;

@Column(name="user_name")
private String name;

@Column(name="user_passwd")
private String password;

@Column(name="user_email")
private String email;

@ElementCollection(fetch= FetchType.EAGER)
@CollectionTable(
name="roles",
joinColumns = @JoinColumn(name="user_id")
)
@Column(name="user_role")
private List<String> roles;

}
UserRepository.java
package com.dev.springboot.security.UserDetailsService.repo;

import java.util.Optional;

import org.springframework.data.jpa.repository.JpaRepository;

import com.dev.springboot.security.UserDetailsService.model.User;

public interface UserRepository extends JpaRepository<User, Integer> {

Optional<User> findUserByEmail(String email);
}

Step#4: Create AppConfig class to instantiate BCryptPasswordEncoder

As BCryptPasswordEncoder is a predefined class, hence we need to provide itโ€™s instantiation code in AppConfig.java as a configuration class. Further, BCryptPasswordEncoder will be required to encode our password values in other classes.

AppConfig.java
package com.dev.springboot.security.UserDetailsService.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

@Configuration
public class AppConfig {

@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}

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.

IUserService.java
package com.dev.springboot.security.UserDetailsService.service;

import com.dev.springboot.security.UserDetailsService.model.User;

public interface IUserService {

public Integer saveUser(User user);
}
UserServiceImpl.java
package com.dev.springboot.security.UserDetailsService.service.impl;

import java.util.Optional;
import java.util.stream.Collectors;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import com.dev.springboot.security.UserDetailsService.model.User;
import com.dev.springboot.security.UserDetailsService.repo.UserRepository;
import com.dev.springboot.security.UserDetailsService.service.IUserService;

@Service
public class UserServiceImpl implements IUserService, UserDetailsService{

@Autowired
private UserRepository userRepo;

@Autowired
private BCryptPasswordEncoder passwordEncoder;

@Override
public Integer saveUser(User user) {
String passwd= user.getPassword();
String encodedPasswod = passwordEncoder.encode(passwd);
user.setPassword(encodedPasswod);
user = userRepo.save(user);
return user.getId();
}

@Override
public UserDetails loadUserByUsername(String email)
throws UsernameNotFoundException {

Optional<User> opt = userRepo.findUserByEmail(email);

if(opt.isEmpty())
throw new UsernameNotFoundException("User with email: " +email +" not found !");
else {
User user = opt.get();
return new org.springframework.security.core.userdetails.User(
user.getEmail(),
user.getPassword(),
user.getRoles()
.stream()
.map(role-> new SimpleGrantedAuthority(role))
.collect(Collectors.toSet())
);
}

}

//Other Approach: Without Using Lambda & Stream API Of Java 8

/** @Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {

Optional<User> opt = userRepo.findUserByEmail(email);

org.springframework.security.core.userdetails.User springUser=null;

if(opt.isEmpty()) {
throw new UsernameNotFoundException("User with email: " +email +" not found");
}
User user =opt.get();
List<String> roles = user.getRoles();
Set<GrantedAuthority> ga = new HashSet<>();
for(String role:roles) {
ga.add(new SimpleGrantedAuthority(role));
}

springUser = new org.springframework.security.core.userdetails.User(
email,
user.getPassword(),
ga );
return springUser;
} */

}

Step#6: Create a UserController class

Subsequently, write a controller class for User as โ€˜UserController.javaโ€™ which will control the user registration page.

UserController.java
package com.dev.springboot.security.UserDetailsService.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;

import com.dev.springboot.security.UserDetailsService.model.User;
import com.dev.springboot.security.UserDetailsService.service.IUserService;

@Controller
public class UserController {

@Autowired
private IUserService userService;

// Go to Registration Page
@GetMapping("/register")
public String register() {
return "registerUser";
}

// Read Form data to save into DB
@PostMapping("/saveUser")
public String saveUser(
@ModelAttribute User user,
Model model
)
{
Integer id = userService.saveUser(user);
String message = "User '"+id+"' saved successfully !";
model.addAttribute("msg", message);
return "registerUser";
}
}

Step#7: Write a Controller class to navigate through pages

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.

HomeController.java
package com.dev.springboot.security.UserDetailsService.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class HomeController {

@GetMapping("/home")
public String getHomePage() {
return "homePage";
}

@GetMapping("/welcome")
public String getWelcomePage() {
return "welcomePage";
}

@GetMapping("/admin")
public String getAdminPage() {
return "adminPage";
}

@GetMapping("/emp")
public String getEmployeePage() {
return "empPage";
}

@GetMapping("/mgr")
public String getManagerPage() {
return "mgrPage";
}

@GetMapping("/hr")
public String getHrPage() {
return "hrPage";
}

@GetMapping("/common")
public String getCommonPage() {
return "commonPage";
}

@GetMapping("/accessDenied")
public String getAccessDeniedPage() {
return "accessDeniedPage";
}
}

Step#8: Write UI pages(Thymeleaf)ย 

Below are the .html files for UI pages. Place these pages inside โ€˜src/main/resources/templatesโ€™ folder accordingly.

registerUser.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="ISO-8859-1">
<title>User Registration</title>
</head>
<body>
<h3>User Registration</h3>
<form action="saveUser" method="post">
<pre>
Name : <input type="text" name="name"/>

Email: <input type="text" name="email"/>

Password: <input type="password" name="password"/>

Role(s): <input type="checkbox" name="roles" value="Admin"/>Admin
<input type="checkbox" name="roles" value="Manager"/>Manager
<input type="checkbox" name="roles" value="HR"/>HR
<input type="checkbox" name="roles" value="Employee"/>Employee
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/>
<input type="submit" value="Register"/>

</pre>
</form>
<div th:text="${msg}"></div>
</body>
</html>
homePage.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<h3> welcome to the Home Page </h3>
This page is accessible to ALL.
</body>
</html>
welcomePage.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<h3> Welcome Page after successful Login</h3>
<a th:href="@{/logout}" >LOGOUT</a>
</body>
</html>
adminPage.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<h3> Admin Page </h3>
Welcome to Admin page.!!!
<a th:href="@{/logout}" >LOGOUT</a>
</body>
</html>
empPage.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<h3> Employee Page </h3>
<a th:href="@{/logout}" >LOGOUT</a>
</body>
</html>
mgrPage.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<h3> Manager Page </h3>
<a th:href="@{/logout}" >LOGOUT</a>
</body>
</html>
hrPage.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<h3> HR Page </h3>
<a th:href="@{/logout}" >LOGOUT</a>
</body>
</html>
commonPage.html
	<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<h3>You are not allowed to access this page. Please go to Welcome Page</h3>
<a th:href="@{/welcome}" >Welcome</a>
<a th:href="@{/logout}" >LOGOUT</a>
</body>
</html>
accessDeniedPage.html
	<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<h3>You are not allowed to access this page. Please go to Welcome Page</h3>
<a th:href="@{/welcome}" >Welcome</a>
<a th:href="@{/logout}" >LOGOUT</a>
</body>
</html>

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.

SecurityConfig.java
package com.dev.springboot.security.UserDetailsService.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

@EnableWebSecurity
@Configuration
public class SecurityConfig {

@Autowired
private UserDetailsService uds;

@Autowired
private BCryptPasswordEncoder encoder;

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {

http.authorizeHttpRequests()
.requestMatchers("/home","/register","/saveUser").permitAll()
.requestMatchers("/welcome").authenticated()
.requestMatchers("/admin").hasAuthority("Admin")
.requestMatchers("/mgr").hasAuthority("Manager")
.requestMatchers("/emp").hasAuthority("Employee")
.requestMatchers("/hr").hasAuthority("HR")
.requestMatchers("/common").hasAnyAuthority("Employeee", "Manager", "Admin")
.anyRequest().authenticated()

.and()
.formLogin()
.defaultSuccessUrl("/welcome",true)

.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))

.and()
.exceptionHandling()
.accessDeniedPage("/accessDenied")

.and()
.authenticationProvider(authenticationProvider());

return http.build();

}

@Bean
public AuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setUserDetailsService(uds);
authenticationProvider.setPasswordEncoder(encoder);
return authenticationProvider;
}
}

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;โ€™.

Troubleshooting

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.

In order to resolve this error, update the version of Thymeleaf as shown below.

<dependency>
     <groupId>org.thymeleaf.extras</groupId>
     <artifactId>thymeleaf-extras-springsecurity5</artifactId>
     <version>3.0.4.RELEASE</version>
</dependency>

Once the update is done, maven will automatically download the competent dependencies.

Summary

After going through all the theoretical & examples part of โ€˜Spring Security UserDetailsService Using Spring Boot 3โ€™, finally, we should be able to implement role based web security in a Spring Boot project. Of course, In this article we have covered the third way of implementing security feature. Similarly, we will talk about some more ways of security when it comes into the picture in our upcoming articles. If there is any change in the future, we will update the same accordingly. If you want to know Whatโ€™s new In Spring Boot 3.0, kindly visit our separate article on โ€˜New Features In Spring Boot 3โ€˜. Moreover, Feel free to provide your comments in the comments section.

ย 

3 thoughts on โ€œSpring Security UserDetailsService Using Spring Boot 3โ€

  1. Great post! Iโ€™m glad to see a detailed explanation of how to implement UserDetailsService in Spring Security using Spring Boot 3. Itโ€™s always helpful to see examples and code snippets to help me understand how to implement security features in my own projects. Thanks for sharing your knowledge!

Leave a Reply


Top