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
- Navigate to Spring Initializr: Open Spring Initializr in your browser.
- 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
- Add Dependencies:
- Spring Web
- Spring Security
- Thymeleaf
- Spring Data JPA
- MySQL Driver
- Lombok
- Generate the Project: Click on the Generate button to download the project as a zip file.
- Extract the Zip File: Extract the zip file to your preferred location.
Using an IDE
- Open Your IDE: Open your preferred IDE (IntelliJ IDEA, Eclipse, etc.).
- 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
- Run the Application: Use your IDE to run the
SpringSecuritDatabaseAuthApplicationclass. - 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
- Navigate to the Login Page: Enter http://localhost:8080 in the browser.
- Login: Enter a username as
adminand password asadmin, then click the Submit button.
- After a successful login, you will see the welcome page.

Built-In Logout Feature
- 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.