Open In App

Implementing Database Authentication and Authorization with Spring Security 6

Last Updated : 06 Aug, 2024
Comments
Improve
Suggest changes
Like Article
Like
Report

Spring Security 6 has made setting up security in Spring applications easier and more straightforward by moving away from the older WebSecurityConfigurerAdapter method. This new approach helps you configure security in a clearer and more flexible way. In this article, we will learn how to set up user login (authentication) and permissions (authorization) in a Spring Boot 3.0 app using this updated framework. We will focus on how to use a database to handle user information, which helps in managing security more effectively.

With this new method, managing user security becomes simpler and more scalable. It makes it easier to set up and adjust security features as your application grows, keeping your authentication and authorization processes secure and up-to-date.

Authentication Methods

This article will focus on database authentication for Spring Security 6.

Flow of Authentication in Spring Security


This diagram shows the basic authentication flow.

  • An unauthenticated object requests access.
  • The Authentication Provider processes the request.
  • If successful, it produces an authenticated object.
  • The arrows show the transformation from unauthenticated to authenticated state.

This represents how a system verifies and grants access to users or services.

Implementation Steps for Database Authentication and Authorization with Spring Security 6

Step 1: Create a New Spring Boot Project

Use Spring Initializr to generate a new Spring Boot project with the following configurations:

  • Project: Maven
  • Language: Java
  • Packaging: Jar
  • Java Version: 17

Include the following dependencies:

  • Spring Web
  • Spring Security
  • Spring Data JPA
  • MySQL Connector
  • Lombok (optional)

For the complete configuration, you can refer to the Maven Repository to find the exact versions and additional details.

Step 2: Set up Application.properties

Configure your application.properties file with necessary settings for your database and Spring Security.

Application Properties


Step 3: Create the User Model

In this step, we will define the User model that represents the user entity in your database and implements the UserDetails interface from Spring Security. This model is crucial for integrating database-driven authentication with Spring Security.

Java
import lombok.*; // Import Lombok annotations
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import javax.persistence.*;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

/**
 * Represents a user in the system with security details.
 */
@Entity
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
public class User implements UserDetails {
    // Delimiter used to split authorities string
    private static final String AUTHORITIES_DELIMITER = "::";

    // Unique identifier for the user
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    // Username of the user
    private String username;

    // Password of the user
    private String password;

    // Authorities granted to the user, stored as a single string
    private String authorities;

    /**
     * Returns the authorities granted to the user.
     * @return a collection of GrantedAuthority objects
     */
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        // Split the authorities string and convert to a list of SimpleGrantedAuthority objects
        return Arrays.stream(this.authorities.split(AUTHORITIES_DELIMITER))
                     .map(SimpleGrantedAuthority::new)
                     .collect(Collectors.toList());
    }

    /**
     * Returns the password used to authenticate the user.
     * @return the password
     */
    @Override
    public String getPassword() {
        return password;
    }

    /**
     * Returns the username used to authenticate the user.
     * @return the username
     */
    @Override
    public String getUsername() {
        return username;
    }

    /**
     * Indicates whether the user's account has expired.
     * @return true if the account is non-expired, false otherwise
     */
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    /**
     * Indicates whether the user is locked or unlocked.
     * @return true if the account is non-locked, false otherwise
     */
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    /**
     * Indicates whether the user's credentials have expired.
     * @return true if the credentials are non-expired, false otherwise
     */
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    /**
     * Indicates whether the user is enabled.
     * @return true if the user is enabled, false otherwise
     */
    @Override
    public boolean isEnabled() {
        return true;
    }
}


The User class represents a user entity in a Spring Security context, implementing the UserDetails interface. It provides user information such as username, password, and authorities. The getAuthorities method converts a delimited string of authorities into a collection of GrantedAuthority objects for security roles.

What is UserDetails Interface?

The UserDetails interface is a core part of Spring Security. It provides a way to retrieve user information necessary for authentication and authorization. When you implement this interface, you tell Spring Security how to get details about a user from your data source (e.g., a database).

Importance:

  • Authentication: Verifies user credentials.
  • Authorization: Provides user roles and permissions.

Key Methods

  • getAuthorities(): Returns user roles/permissions.
  • getPassword(): Returns the user’s password.
  • getUsername(): Returns the username.
  • isAccountNonExpired(): Indicates if the account is expired (defaults to true).
  • isAccountNonLocked(): Indicates if the account is locked (defaults to true).
  • isCredentialsNonExpired(): Indicates if credentials are expired (defaults to true).
  • isEnabled(): Indicates if the user is enabled.

In Spring Security 6, methods like isAccountNonExpired(), isAccountNonLocked(), and isCredentialsNonExpired() return true by default unless overridden.

Step 4: Define the User Service

Create a service to handle user details and authentication. This service implements the UserDetailsService interface, essential for fetching user information during authentication.

Java
import com.example.SpringSecurity.Spring.Demo.Project.Model.User;
import com.example.SpringSecurity.Spring.Demo.Project.Repository.UserRepo;
import org.springframework.beans.factory.annotation.Autowired;
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;

@Service // Marks this class as a service component for dependency injection
public class UserService implements UserDetailsService {

    @Autowired
    private UserRepo userRepo; // Injects the UserRepo for accessing user data

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // Retrieves user details by username from the database
        return userRepo.findByUsername(username);
    }

    public String create(String username, String password) {
        // Encodes the password and creates a new User object
        User user = User.builder()
                .username(username)
                .password(new BCryptPasswordEncoder().encode(password)) // Encrypts the password
                .authorities("student") // Assigns default authority
                .build();
        
        // Saves the new user to the database
        userRepo.save(user);
        
        return "Create Successfully !"; // Returns a success message
    }
}


Why this Service is Implemented?

  • Loading User Details:
    • Method: loadUserByUsername(String username)
    • Purpose: This method fetches user information from the database using their username. Spring Security calls this method to retrieve the user's details when they attempt to log in.
  • Creating Users:
    • Method: create(String username, String password)
    • Purpose: This method creates a new user and saves their information to the database. It uses BCryptPasswordEncoder to securely encode the user's password before storing it.

Explanation:

  • UserRepo: This is the database repository used to get and save user information.
  • UserDetailsService: This is an interface from Spring Security that helps load user data for authentication.
  • BCryptPasswordEncoder: This tool encrypts passwords to keep them safe when stored in the database.

In essence, this service is crucial because it bridges the gap between your application's user data and Spring Security, ensuring secure and smooth authentication processes.

Step 5: Define the User Repository

In this step, we create a repository interface for accessing user data in the database.

Java
import com.example.SpringSecurity.Spring.Demo.Project.Model.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.security.core.userdetails.UserDetails;

public interface UserRepo extends JpaRepository<User, Integer> {
    UserDetails findByUsername(String username);
}

The UserRepo interface simplifies database interactions for user data and provides a way to retrieve user details by username, which is essential for authenticating users in your application

Step 6: Create the Security Controller

In this step, we define a controller to handle HTTP requests and interact with the UserService.

Java
import com.example.SpringSecurity.Spring.Demo.Project.Service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/test")
public class SecurityController {
    @Autowired
    UserService userService;
    @GetMapping("/home")
    public  String home(){
        return "This is Home";
    }
    @GetMapping("/student")
    public  String student(){
        return "This is Student";
    }
    @GetMapping("/admin")
    public  String admin(){
        return "This is Admin";
    }
    @PostMapping("/create")
    public String create(@RequestParam("username") String username, @RequestParam("password") String password){
       return  userService.create(username, password);
    }
}

Controller Overview:

  • Endpoints:
    • /home: Accessible by everyone.
    • /student: Restricted to users with the "STUDENT" authority.
    • /admin: Restricted to users with the "ADMIN" authority.
    • /create: Allows creation of new users by accepting username and password.
  • Purpose: Handles user-related requests, returns simple messages, and allows user creation.

Final and Important Step: Configure Spring Security

In this step, we set up the security configuration for our Spring Boot application. This involves defining how authentication and authorization are handled, and securing our endpoints.

Java
import com.example.SpringSecurity.Spring.Demo.Project.Service.UserService;
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.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Autowired
    private UserService userService;
    @Bean
    public AuthenticationProvider authenticationProvider(){
        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
        provider.setUserDetailsService(userService);
        provider.setPasswordEncoder(passwordEncoder());
        return provider;
    }
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
        httpSecurity
                .csrf(httpSecurityCsrfConfigurer -> httpSecurityCsrfConfigurer.disable())
                .httpBasic(Customizer.withDefaults())
                .authorizeHttpRequests(
                        authorizeRequest -> authorizeRequest
                                .requestMatchers("/test/student").hasAuthority("student")
                                .requestMatchers("/test/admin").hasAuthority("admin")
                                .anyRequest().permitAll()
                ).formLogin(Customizer.withDefaults());
        return httpSecurity.build();
    }


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

Configuration Breakdown:

  • Enable Web Security: @Configuration and @EnableWebSecurity enable Spring Security in the application.
  • Authentication Provider: authenticationProvider() sets up the provider using DaoAuthenticationProvider to handle user details and password encoding.
  • Security Filter Chain: securityFilterChain(HttpSecurity httpSecurity) configures HTTP security, including CSRF protection, basic authentication, and request authorization.
  • Password Encoder: passwordEncoder() provides a bean for encoding passwords with BCrypt.

Authentication and Authorization Results

Step 1: Start The Server

Step 2: Check First Public endpoint it's working or not

Accesing the "/home " Endpont of the controller i.e public


Step 3: Check All secured Endpoints working or not

When we try to access any secured endpoint it's redirect to login page.

Sign in Page


Let's try to access with Any Username & password:

When We are trying to give wrong usename & password it'll fetch the error.

Sign in Error Page


Let's See the what is username & password are stored in our MySQL tables.

Empty User Table


Create a new User.

User Created Successfully

This user is always have student authority int our case because ewhen we creating the new user this always student authorites our service logic because we want to secure application so we can't give access any user to define it's authority for admin we create admin from database layer or you can define seprate admin for your controller according to you need.

Now let's again check the Database,

Database Information


We Create Admin manually for security purpose now let's check using this data can we login the endpoints.

Login as Student

Output:

Browser Output after login
Booom ! we Successfully Loged In


If we try to login with who has admin authority, is it login? let's check,

Login

Output:

It returns 403 as status with type Forbidden.

Output


And we are also expecting this because it's unauthorized for user so we cannot access with wrong credential, now let's try to login admin page with admin credential.

We Are trying to access admin endpoint

Output:

Login as Admin

Conclusion

In this article, we covered how to implement authentication and authorization in a Spring Boot 3.0 application using Spring Security 6. We moved from the old WebSecurityConfigurerAdapter approach to a more modern, component-based setup. Key steps included:

  • Creating a User entity and configuring it with UserDetails for user data.
  • Setting up a UserService to handle user management and authentication.
  • Defining a UserRepo for database interactions.
  • Building a SecurityController with endpoints that are secured based on user roles.
  • Configuring SecurityConfig to manage authentication, authorization, and password encoding.

This setup ensures that your application is both secure and scalable, with proper management of user roles and credentials.


Next Article

Similar Reads