Spring Boot Security Database Authentication Example

In this Spring Security tutorial, we will learn how to implement a custom login form with database authentication using Java, Spring Boot, Spring Security, and a MySQL database. In database authentication, the user enters login credentials (username and password) in a login form and submits it. The application then validates the entered credentials with those stored in the database.

Step 1: Create a Spring Boot Project

Using Spring Initializr

  1. Navigate to Spring Initializr: Open Spring Initializr in your browser.
  2. Configure the Project:
    • Project: Maven Project
    • Language: Java
    • Spring Boot: 3.2
    • Group: com.rameshfadatare
    • Artifact: springsecuritydatabaseauth
    • Name: springsecuritydatabaseauth
    • Description: Demo project for Spring Boot Security Database Authentication
    • Package name: com.rameshfadatare.springsecuritydatabaseauth
    • Packaging: Jar
    • Java: 17
  3. Add Dependencies:
    • Spring Web
    • Spring Security
    • Thymeleaf
    • Spring Data JPA
    • MySQL Driver
    • Lombok
  4. Generate the Project: Click on the Generate button to download the project as a zip file.
  5. Extract the Zip File: Extract the zip file to your preferred location.

Using an IDE

  1. Open Your IDE: Open your preferred IDE (IntelliJ IDEA, Eclipse, etc.).
  2. Import the Project: Import the extracted project as a Maven project.

Step 2: Add Maven Dependencies

Ensure your pom.xml contains the necessary dependencies. It should look something like this:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>com.mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
</dependencies>

Step 3: Configure MySQL Database

Create a Database

Create a database in your MySQL server using the following command:

create database login_system;

Configure Database Properties

Add the following properties to the src/main/resources/application.properties file:

spring.datasource.url=jdbc:mysql://localhost:3306/login_system
spring.datasource.username=root
spring.datasource.password=root

# Hibernate ddl auto (create, create-drop, validate, update)
spring.jpa.hibernate.ddl-auto=update

logging.level.org.springframework.security=DEBUG

Step 4: Create JPA Entities

User Entity

Create a User class under the com.rameshfadatare.springsecuritydatabaseauth.model package:

package com.rameshfadatare.springsecuritydatabaseauth.model;

import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.util.Set;

@Setter
@Getter
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "users")
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    @Column(nullable = false, unique = true)
    private String username;
    @Column(nullable = false, unique = true)
    private String email;
    @Column(nullable = false)
    private String password;

    @ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @JoinTable(name = "users_roles",
            joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"),
            inverseJoinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id")
    )
    private Set<Role> roles;
}

Role Entity

Create a Role class under the com.rameshfadatare.springsecuritydatabaseauth.model package:

package com.rameshfadatare.springsecuritydatabaseauth.model;

import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "roles")
public class Role {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
}

Step 5: Create Repositories

UserRepository

Create a UserRepository interface under the com.rameshfadatare.springsecuritydatabaseauth.repository package:

package com.rameshfadatare.springsecuritydatabaseauth.repository;

import com.rameshfadatare.springsecuritydatabaseauth.model.User;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;

public interface UserRepository extends JpaRepository<User, Long> {

    Optional<User> findByUsername(String username);

    Boolean existsByEmail(String email);

    Optional<User> findByUsernameOrEmail(String username, String email);

    boolean existsByUsername(String username);
}

RoleRepository

Create a RoleRepository interface under the com.rameshfadatare.springsecuritydatabaseauth.repository package:

package com.rameshfadatare.springsecuritydatabaseauth.repository;

import com.rameshfadatare.springsecuritydatabaseauth.model.Role;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;

public interface RoleRepository extends JpaRepository<Role, Long> {
    Optional<Role> findByName(String name);
}

Step 6: Create Custom UserDetailsService

Create a CustomUserDetailsService class under the com.rameshfadatare.springsecuritydatabaseauth.service package:

package com.rameshfadatare.springsecuritydatabaseauth.service;

import com.rameshfadatare.springsecuritydatabaseauth.model.User;
import com.rameshfadatare.springsecuritydatabaseauth.repository.UserRepository;
import lombok.AllArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
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.stereotype.Service;

import java.util.Set;
import java.util.stream.Collectors;

@Service
@AllArgsConstructor
public class CustomUserDetailsService implements UserDetailsService {

    private UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String usernameOrEmail) throws UsernameNotFoundException {
        User user = userRepository.findByUsernameOrEmail(usernameOrEmail, usernameOrEmail)
                .orElseThrow(() -> new UsernameNotFoundException("User not exists by Username or Email"));

        Set<GrantedAuthority> authorities = user.getRoles().stream()
                .map((role) -> new SimpleGrantedAuthority(role.getName()))
                .collect(Collectors.toSet());

        return new org.springframework.security.core.userdetails.User(
                usernameOrEmail,
                user.getPassword(),
                authorities
        );
    }
}

Step 7: Spring Security Configuration

Create a SpringSecurityConfig class under the com.rameshfadatare.springsecuritydatabaseauth.config package:

package com.rameshfadatare.springsecuritydatabaseauth.config;

import lombok.AllArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

@Configuration
@AllArgsConstructor
public class SpringSecurityConfig {

    private UserDetailsService userDetailsService;

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

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeHttpRequests((authorize) ->
                        authorize.anyRequest().authenticated()
                ).formLogin(
                        form -> form
                                .loginPage("/login")
                                .loginProcessingUrl("/login")
                                .defaultSuccessUrl("/welcome")
                                .permitAll()
                ).logout(
                        logout -> logout
                                .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
                                .permitAll()
                );
        return http.build();
    }

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception {
        return configuration.getAuthenticationManager();
    }
}

Step 8: Create Thymeleaf Templates

Login Page – login.html

Under the /src/main/resources/templates folder, create a login.html file and add the following content:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Login System</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css"
          rel="stylesheet"
          integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC"
          crossorigin="anonymous">
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
    <div class="container-fluid">
        <a class="navbar-brand" th:href="@{/index}">Spring Security Custom Login Example</a>
        <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
            <span class="navbar-toggler-icon"></span>
        </button>
    </div>
</nav>
<br /><br />
<div class="container">
    <div class="row">
        <div class="col-md-6 offset-md-3">

            <div th:if="${param.error}">
                <div class="alert alert-danger">Invalid Email or Password</div>
            </div>
            <div th:if="${param.logout}">
                <div class="alert alert-success"> You have been logged out.</div>
            </div>

            <div class="card">
                <div class="card-header">
                    <h2 class="text-center">Login Form</h2>
                </div>
                <div class="card-body">
                    <form method="post" role="form" th:action="@{/login}" class="form-horizontal">
                        <div class="form-group mb-3">
                            <label class="control-label"> Email</label>
                            <input type="text" id="username" name="username" class="form-control" placeholder="Enter email address"/>
                        </div>

                        <div class="form-group mb-3">
                            <label class="control-label"> Password</label>
                            <input type="password" id="password" name="password" class="form-control" placeholder="Enter password"/>
                        </div>
                        <div class="form-group mb-3">
                            <button type="submit" class="btn btn-primary">Submit</button>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
</body>
</html>

Welcome Page – welcome.html

Under the /src/main/resources/templates folder, create a welcome.html file and add the following content:

<!DOCTYPE html>
<html lang="en"
      xmlns:th="http://www.thymeleaf.org"
>
<head>
    <meta charset="UTF-8">
    <title>Registration and Login System</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css"
          rel="stylesheet"
          integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC"
          crossorigin="anonymous">
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
    <div class="container-fluid">
        <a class="navbar-brand" th:href="@{/index}">Spring Security Custom Login Example</a>
        <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
            <span class="navbar-toggler-icon"></span>
        </button>
        <div class="collapse navbar-collapse" id="navbarSupportedContent">
            <ul class="navbar-nav me-auto mb-2 mb-lg-0">
                <li class="nav-item">
                    <a class="nav-link active" aria-current="page" th:href="@{/logout}">Logout</a>
                </li>
            </ul>
        </div>
    </div>
</nav>
<br /><br />
<div class="container">
    <div class="row">
        <h1> Welcome to Spring Security world!</h1>
    </div>
</div>
</body>
</html>

Step 9: Create Spring MVC Controller

Create a WelComeController class under the com.rameshfadatare.springsecuritydatabaseauth.controller package:

package com.rameshfadatare.springsecuritydatabaseauth.controller;

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

@Controller
public class WelComeController {

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

    @GetMapping("/login")
    public String login() {
        return "login";
    }
}

Step 10: Insert SQL Scripts

Before testing Spring Security, use the following SQL scripts to insert data into the respective tables:

INSERT INTO users VALUES
(1,'ramesh@gmail.com','ramesh','$2a$10$5PiyN0MsG0y886d8xWXtwuLXK0Y7zZwcN5xm82b4oDSVr7yF0O6em','ramesh'),
(2,'admin@gmail.com','admin','$2a$10$gqHrslMttQWSsDSVRTK1OehkkBiXsJ/a4z2OURU./dizwOQu5Lovu','admin');

INSERT INTO roles VALUES (1,'ROLE_ADMIN'),(2,'ROLE_USER');

INSERT INTO users_roles VALUES (2,1),(1,2);

Hibernate will automatically create the database tables, so you don’t need to create the tables manually.

Step 11: Run the Application

  1. Run the Application: Use your IDE to run the SpringSecuritDatabaseAuthApplication class.
  2. Access the Application: Open your browser and go to http://localhost:8080. You should see the custom login page.

Step 12: Test the Custom Login Page

  1. Navigate to the Login Page: Enter http://localhost:8080 in the browser.
  2. Login: Enter a username as admin and password as admin, then click the Submit button. Spring Boot Security Database Authentication Example
  3. After a successful login, you will see the welcome page. Spring Boot Security Database Authentication Example

Built-In Logout Feature

  1. Click on the logout button in the application to log out.

Conclusion

In this Spring Security tutorial, we learned how to implement a custom login form with database authentication using Spring Security and a MySQL database.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top