In this fully updated tutorial, you’ll learn how to build a secure login and registration web application using Spring Boot 3.5.4, Spring Security 6, MongoDB, and Thymeleaf for the frontend view layer. We'll implement form-based authentication, user role-based authorization, password hashing with BCrypt, and a clean UI styled with Bootstrap. By the end, you'll have a working full-stack Java web app with modern Spring features and MongoDB integration.
Prerequisites
Make sure you have:
-
Java 21 or 17
-
Maven 3.9+
-
MongoDB running locally or in Docker
-
IDE (IntelliJ, VS Code, etc.)
You can watch the video tutorial on our YouTube channel, featuring the latest Spring Boot version.
Project Setup with Spring Boot 3.5.4
We’ll start by creating a new Spring Boot project using Spring Initializr or your IDE (e.g., IntelliJ IDEA, Eclipse).
✅ Dependencies to Include:
-
Spring Web
-
Spring Security
-
Spring Data MongoDB
-
Thymeleaf
-
Validation (Jakarta Bean Validation)
-
Lombok (optional, for brevity)
Generate and then extract the ZIP file to your Project folder.
🔧 pom.xml
(if using Maven)
Here’s the trimmed version of your pom.xml
with updated dependencies:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.5.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
Application Properties
Edit src/main/resources/application.properties
:
spring.application.name=secureweb
spring.data.mongodb.database=springsecuritydb
spring.data.mongodb.uri=mongodb://localhost:27017/springsecuritydb
spring.thymeleaf.cache=false
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.mvc.view.prefix=/templates/
spring.mvc.view.suffix=.html
Make sure MongoDB is running locally.
MongoDB User and Role Models
We’ll define two documents:
-
Role
: to store user roles (ROLE_USER
,ROLE_ADMIN
, etc.) -
User
: to store user credentials, roles, and other profile data
Create a new Java package and class files model/Role.java
and model/User.java
.
🧩 Role.java
package com.djamware.secureweb.model;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import lombok.Getter;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@RequiredArgsConstructor
@Document
public class Role {
@Id
private String id;
@NonNull
private String name;
}
🧩 User.java
package com.djamware.secureweb.model;
import java.util.Set;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
@Document
public class User {
@Id
private String id;
private String username;
private String password;
private String email;
private Set<Role> roles;
}
Repositories
We’ll use Spring Data MongoDB interfaces. Create a new Java package and interface files repository/RoleRepository.java
and repository/UserRepository.java
.
🗃️ RoleRepository.java
package com.djamware.secureweb.repository;
import org.springframework.data.mongodb.repository.MongoRepository;
import com.djamware.secureweb.model.Role;
public interface RoleRepository extends MongoRepository<Role, String> {
Role findByName(String name);
}
🗃️ UserRepository.java
package com.djamware.secureweb.repository;
import org.springframework.data.mongodb.repository.MongoRepository;
import com.djamware.secureweb.model.User;
public interface UserRepository extends MongoRepository<User, String> {
User findByUsername(String username);
}
Spring Security Configuration (Spring Boot 3.5.4 + Spring Security 6)
Create a Java package and class file config/AuthManagerConfig
.java
.
📁 AuthManagerConfig
.java
package com.djamware.secureweb.config;
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;
@Configuration
public class AuthManagerConfig {
@Bean
public AuthenticationManager authenticationManager(
AuthenticationConfiguration authConfig) throws Exception {
return authConfig.getAuthenticationManager();
}
}
Create a Java package and class file config/SecurityConfig.java
.
📁 SecurityConfig.java
package com.djamware.secureweb.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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 {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/login", "/register", "/css/**", "/js/**").permitAll()
.anyRequest().authenticated())
.formLogin(form -> form
.loginPage("/login")
.defaultSuccessUrl("/", true)
.permitAll())
.logout(logout -> logout
.logoutSuccessUrl("/login?logout")
.permitAll());
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
CustomUserDetailsService
Implementation
We need to integrate MongoDB with Spring Security by implementing UserDetailsService
. Create a new Java package and file service/CustomUserDetailsService
.java.
package com.djamware.secureweb.service;
import java.util.stream.Collectors;
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 com.djamware.secureweb.model.Role;
import com.djamware.secureweb.model.User;
import com.djamware.secureweb.repository.UserRepository;
@Service
public class CustomUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
public CustomUserDetailsService(UserRepository userRepo) {
this.userRepository = userRepo;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User appUser = userRepository.findByUsername(username);
if (appUser == null) {
throw new UsernameNotFoundException("User not found");
}
return new org.springframework.security.core.userdetails.User(
appUser.getUsername(),
appUser.getPassword(),
appUser.getRoles().stream()
.map(Role::getName)
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toSet()));
}
}
Registration and Login Forms with Thymeleaf
📁 src/main/resources/templates/register.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Register</title>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="bg-light">
<div class="container mt-5">
<h2>Register</h2>
<form th:action="@{/register}" method="post" th:object="${user}">
<div class="mb-3">
<label for="username" class="form-label">Username</label>
<input type="text" th:field="*{username}" class="form-control" id="username" required>
</div>
<div class="mb-3">
<label for="password" class="form-label">Password</label>
<input type="password" th:field="*{password}" class="form-control" id="password" required>
</div>
<button type="submit" class="btn btn-primary">Register</button>
<a href="/login" class="btn btn-link">Already have an account?</a>
</form>
</div>
</body>
</html>
📁 src/main/resources/templates/login.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Login</title>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="bg-light">
<div class="container mt-5">
<h2>Login</h2>
<form th:action="@{/login}" method="post">
<div th:if="${param.error}" class="alert alert-danger">Invalid username or password.</div>
<div th:if="${param.logout}" class="alert alert-success">You have been logged out.</div>
<div class="mb-3">
<label for="username" class="form-label">Username</label>
<input type="text" name="username" class="form-control" id="username" required>
</div>
<div class="mb-3">
<label for="password" class="form-label">Password</label>
<input type="password" name="password" class="form-control" id="password" required>
</div>
<button type="submit" class="btn btn-success">Login</button>
<a href="/register" class="btn btn-link">Don't have an account?</a>
</form>
</div>
</body>
</html>
With these two forms in place:
-
/register
renders the registration form and handles form submission. -
/login
is handled by Spring Security for authentication. -
We’re using Bootstrap 5 CDN for styling, and Thymeleaf's
th:field
/th:object
bindings.\
User Registration Controller and MongoDB Persistence
✅ Update UserRepository.java
package com.djamware.secureweb.repository;
import org.springframework.data.mongodb.repository.MongoRepository;
import com.djamware.secureweb.model.User;
public interface UserRepository extends MongoRepository<User, String> {
User findByUsername(String username);
boolean existsByUsername(String username);
}
✅ Create a new Java interface file service/UserService.java
package com.djamware.secureweb.service;
import com.djamware.secureweb.model.User;
public interface UserService {
void register(User user);
}
✅ Create a new Java class file service/UserServiceImpl.java
package com.djamware.secureweb.service;
import java.util.Collections;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import com.djamware.secureweb.model.Role;
import com.djamware.secureweb.model.User;
import com.djamware.secureweb.repository.RoleRepository;
import com.djamware.secureweb.repository.UserRepository;
@Service
public class UserServiceImpl implements UserService {
private final UserRepository userRepository;
private final RoleRepository roleRepository;
private final PasswordEncoder passwordEncoder;
public UserServiceImpl(UserRepository userRepository,
RoleRepository roleRepository,
PasswordEncoder passwordEncoder) {
this.userRepository = userRepository;
this.roleRepository = roleRepository;
this.passwordEncoder = passwordEncoder;
}
@Override
public void register(User user) {
if (userRepository.existsByUsername(user.getUsername())) {
throw new RuntimeException("Username already exists");
}
Role userRole = roleRepository.findByName("ROLE_USER");
if (userRole == null) {
userRole = new Role("ROLE_USER");
roleRepository.save(userRole);
}
user.setPassword(passwordEncoder.encode(user.getPassword()));
user.setRoles(Collections.singleton(userRole));
userRepository.save(user);
}
}
✅ Create a new Java class controller/AuthController.java
package com.djamware.secureweb.controller;
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.djamware.secureweb.model.User;
import com.djamware.secureweb.service.UserService;
@Controller
public class AuthController {
private final UserService userService;
public AuthController(UserService userService) {
this.userService = userService;
}
@GetMapping("/register")
public String showRegistrationForm(Model model) {
model.addAttribute("user", new User());
return "register";
}
@PostMapping("/register")
public String registerUser(@ModelAttribute("user") User user) {
userService.register(user);
return "redirect:/login";
}
@GetMapping("/login")
public String showLoginForm() {
return "login";
}
}
At this point:
-
New users are registered and persisted in MongoDB.
-
Passwords are encrypted using
BCryptPasswordEncoder
. -
Duplicate usernames are rejected.
-
After registration, users are redirected to the login page.
Securing Routes and Creating a Home Page
✅ SecurityConfig.java
(Updated for Route Authorization)
package com.djamware.secureweb.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import com.djamware.secureweb.service.CustomUserDetailsService;
@Configuration
public class SecurityConfig {
private final CustomUserDetailsService userDetailsService;
public SecurityConfig(CustomUserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/login", "/register", "/css/**", "/js/**").permitAll()
.anyRequest().authenticated())
.formLogin(form -> form
.loginPage("/login")
.defaultSuccessUrl("/", true)
.permitAll())
.logout(logout -> logout
.logoutSuccessUrl("/login?logout")
.permitAll());
return http.build();
}
@Bean
public DaoAuthenticationProvider authProvider() {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(userDetailsService);
provider.setPasswordEncoder(passwordEncoder());
return provider;
}
@Bean
public AuthenticationManager authenticationManager(
AuthenticationConfiguration authConfig) throws Exception {
return authConfig.getAuthenticationManager();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
Explanation:
-
/login
and/register
are public. -
All other routes require authentication.
-
After login, users are redirected to
/
(the home page). -
Logout redirects to login with a
?logout
parameter.
✅ Create a new Java file controller/HomeController.java
package com.djamware.secureweb.controller;
import java.security.Principal;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class HomeController {
@GetMapping("/")
public String home(Model model, Principal principal) {
model.addAttribute("username", principal.getName());
return "index";
}
}
✅ templates/index.html
(Thymeleaf)
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Home - Spring Boot Security</title>
<link rel="stylesheet" th:href="@{/css/bootstrap.min.css}" />
</head>
<body class="container mt-5">
<h1>Welcome, <span th:text="${username}"></span>!</h1>
<p>You are logged in successfully.</p>
<a class="btn btn-danger" th:href="@{/logout}">Logout</a>
</body>
</html>
✅ Optional: CSS Bootstrap file
You can download and place Bootstrap 5 locally in src/main/resources/static/css/bootstrap.min.css
, or use CDN in the layout like:
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
Once this is in place:
-
Only logged-in users can access
/
. -
Anonymous users are redirected to
/login
. -
The home page greets the logged-in user by username.
Creating Login and Registration Templates
These views will allow users to log in and register. They’re styled with Bootstrap for a clean UI and use Thymeleaf syntax for dynamic form handling.
✅ templates/login.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Login</title>
<link rel="stylesheet" th:href="@{/css/bootstrap.min.css}" />
</head>
<body class="container mt-5">
<h2>Login</h2>
<div th:if="${param.logout}" class="alert alert-info">
You have been logged out.
</div>
<div th:if="${param.error}" class="alert alert-danger">
Invalid username or password.
</div>
<form th:action="@{/login}" method="post">
<div class="mb-3">
<label for="username" class="form-label">Username</label>
<input type="text" class="form-control" name="username" required autofocus />
</div>
<div class="mb-3">
<label for="password" class="form-label">Password</label>
<input type="password" class="form-control" name="password" required />
</div>
<button class="btn btn-primary" type="submit">Login</button>
<a class="btn btn-link" th:href="@{/register}">Register</a>
</form>
</body>
</html>
✅ templates/register.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Register</title>
<link rel="stylesheet" th:href="@{/css/bootstrap.min.css}" />
</head>
<body class="container mt-5">
<h2>Register</h2>
<div th:if="${error}" class="alert alert-danger" th:text="${error}"></div>
<form th:action="@{/register}" th:object="${user}" method="post">
<div class="mb-3">
<label class="form-label">Username</label>
<input type="text" class="form-control" th:field="*{username}" required />
</div>
<div class="mb-3">
<label class="form-label">Password</label>
<input type="password" class="form-control" th:field="*{password}" required />
</div>
<button class="btn btn-success" type="submit">Register</button>
<a class="btn btn-link" th:href="@{/login}">Back to Login</a>
</form>
</body>
</html>
With these pages:
-
GET /login
renders the login form. -
POST /login
is handled by Spring Security. -
GET /register
renders the registration form. -
POST /register
creates a new user and redirects to login.
Testing the Full Flow
Make sure everything is wired up correctly:
🔧 1. Start the Application
Run the application from your IDE or using the command line:
./mvnw spring-boot:run
🌐 2. Visit the App in Browser
Open: http://localhost:8080
You should be redirected to the /login
page.
🧪 3. Test Registration
-
Click “Register”
-
Fill in a new username and password.
-
After submission, you should be redirected to the login page.
🔐 4. Test Login
-
Use the registered credentials.
-
You’ll be redirected to the
/
home page (or secured page). -
You'll see your username displayed.
🚪 5. Test Logout
Click the Logout link. You’ll be redirected back to the login page with a logout
message.
✅ Success Criteria:
-
⬜ Can register a new user
-
⬜ User appears in MongoDB with an encrypted password and roles
-
⬜ Can log in using Spring Security
-
⬜ Can access protected routes after login
-
⬜ Logout works correctly
That wraps up the implementation!
Conclusion
In this updated guide, you learned how to build a secure Spring Boot 3.5.4 web application with MongoDB authentication and role-based access control using Spring Security, Thymeleaf, and Bootstrap. We've modernized the 7-year-old original version to reflect the latest Spring ecosystem and best practices, including:
-
Setting up a Spring Boot 3.5.4 project with Spring Security and MongoDB.
-
Creating user and role models with MongoDB repositories.
-
Securing routes using role-based access control.
-
Implementing registration and login functionality.
-
Building Bootstrap-styled Thymeleaf templates.
-
Protecting web routes and handling sessions with logout.
This architecture is ideal for applications that require a lightweight, full-stack Java-based security solution with easy UI integration. You can further expand this project by:
-
Adding remember-me functionality.
-
Enabling CSRF protection with tokens.
-
Supporting email verification or OAuth2 providers.
-
Building an admin dashboard for managing users.
You can get the full source code on our GitHub.
That's just the basics. If you need more in-depth learning about Java and Spring Framework, you can take the following cheap course:
- Comprehensive Java Course Certification Training
- Comprehensive Java Course
- Spring Boot Fundamentals with Unit Testing (MockMVC/Mockito)
- Java Spring Boot Course For Beginners
- Spring Boot Fundamentals
- Reactive Redis Masterclass For Java Spring Boot Developers
- Full Stack CRUD application with Spring Boot and React Hooks
- Spring 6 & Spring Boot 3 for Beginners (Includes 7 Projects)
- SOLID Principles using JAVA
- Advanced Java Dev Interview Preparation
Thanks!