Spring Boot, Security, and Data MongoDB Authentication Example

by Didin J. on Jun 24, 2018 Spring Boot, Security, and Data MongoDB Authentication Example

Step by step tutorial on creating the authentication (login) using Spring Boot, Spring Security, Spring Data and MongoDB with working example.

This tutorial aims to walk through an example of creating the authentication or log in using Spring Boot, Spring Security, Spring Data, and MongoDB for Java web application with custom User Details Service. Using Spring Boot will make web development more compact and faster. Right here, we combined with Netbeans IDE for coding boost. Previously, we showed you how to use Spring Boot, MVC, and MongoDB also Spring Boot, MongoDB RESTful web service. Now, using the same way of tutorial walkthrough we'll be added security to the web application using Spring Security.

A shortcut of the steps:

Spring Security is one of the most used Security frameworks for Java Web Application. We already show you the tutorial on Spring Security with Groovy and Grails. They all the same thing except the core language for development different between Groovy and Java. For this tutorial, the following tools and framework are required:

  1. Java Development Kit (JDK) 8
  2. Gradle
  3. Spring Boot
  4. Spring MVC
  5. Spring Data MongoDB
  6. Spring Security Core
  7. Thymeleaf
  8. Spring Initializer
  9. Netbeans 8.2 or Eclipse
  10. Terminal or cmd

You can watch the video tutorial from our YouTube channel with the latest Spring Boot version.


Generate a New Spring Boot Gradle Project

We assume that you have installed JDK 8, Gradle and IDE (Netbeans or Eclipse). Next, we will create a new Spring Boot Gradle project using Spring Initializer. Just go to Spring Initializer web-based Spring project generator then fill the required frameworks and libraries.

Spring Boot, Security, and Data MongoDB Authentication Example - Spring initializr

After filling all fields, click Generate Project. It will automatically download the zipped project. Next, extract the zipped project to your java projects folder. On the project folder root, you will find `build.gradle` file for register dependencies, initially it looks like this.

buildscript {
    ext {
        springBootVersion = '2.0.2.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

group = 'com.djamware'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8

repositories {
    mavenCentral()
}

dependencies {
    compile('org.springframework.boot:spring-boot-starter-data-mongodb')
    compile('org.springframework.boot:spring-boot-starter-security')
    compile('org.springframework.boot:spring-boot-starter-thymeleaf')
    compile('org.springframework.boot:spring-boot-starter-web')
    testCompile('org.springframework.boot:spring-boot-starter-test')
    testCompile('org.springframework.security:spring-security-test')
}

Next, open the terminal then goes to the newly extracted project folder then type this command.

./gradlew build

It will build and install all Gradle dependencies. To configure Spring Data with MongoDB, simply open and edit `src/resources/application.properties` then add these lines of strings.

spring.data.mongodb.database=springmongodb
spring.data.mongodb.host=localhost
spring.data.mongodb.port=27017


Create Java User and Role Domains or Models or Entities

We will store user and role to the MongoDB collections. For that, we have to create models for User and Role. On the Netbeans 8.2 right-click project name then click New then click Java Class.

Spring Boot, Security, and Data MongoDB Authentication Example - Netbeans New Java Class

Give the class name `User` and the package name `com.springauth.springsecurityauth.domain` then click the finish button.

Spring Boot, Security, and Data MongoDB Authentication Example - Netbeans User Class

It will automatically open the newly created Java Class file. Add these imports of Spring Framework data annotation and the required MongoDB core after the package name.

package com.djamware.springbootmongodbsecurity.domain;

import java.util.Set;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.index.IndexDirection;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.DBRef;
import org.springframework.data.mongodb.core.mapping.Document;

To make this class a Spring Data document and assign to user collection, add this annotation before the class name.

@Document(collection = "user")
public class User {

}

Declare all required fields or variables for user data.

@Id
private String id;
@Indexed(unique = true, direction = IndexDirection.DESCENDING, dropDups = true)
private String email;
private String password;
private String fullname;
private boolean enabled;
@DBRef
private Set<Role> roles;

Create a setter and getter for all above fields. On the Netbeans it can be generated using the menu Source -> Insert Codes then choose getter and setter.

public String getId() {
    return id;
}

public void setId(String id) {
    this.id = id;
}

public String getEmail() {
    return email;
}

public void setEmail(String email) {
    this.email = email;
}

public String getPassword() {
    return password;
}

public void setPassword(String password) {
    this.password = password;
}

public String getFullname() {
    return fullname;
}

public void setFullname(String fullname) {
    this.fullname = fullname;
}

public boolean isEnabled() {
    return enabled;
}

public void setEnabled(boolean enabled) {
    this.enabled = enabled;
}

public Set<Role> getRoles() {
    return roles;
}

public void setRoles(Set<Role> roles) {
    this.roles = roles;
}

Next, do the same thing as the previous step for the role using the name `Role` and same package name `com.springauth.springsecurityauth.domain`. After the file opened, replace all codes with this.

package com.djamware.springbootmongodbsecurity.domain;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.index.IndexDirection;
import org.springframework.data.mongodb.core.index.Indexed;

@Document(collection = "role")
public class Role {

    @Id
    private String id;
    @Indexed(unique = true, direction = IndexDirection.DESCENDING, dropDups = true)

    private String role;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getRole() {
        return role;
    }

    public void setRole(String role) {
        this.role = role;
    }

}


Create Java User and Role Repository

Now, we have to create a repository interface for User and Role domains/models. On the Netbeans, right-click project name then click New then click Java Interface.

Spring Boot, Security, and Data MongoDB Authentication Example - Netbeans Java Interface

Enter class name `UserRepository` and package name `com.springauth.springsecurityauth.repository` then click Finish button.

Spring Boot, Security, and Data MongoDB Authentication Example - Netbeans User Repository

After the newly created Java Interface file opened, replace all codes with this.

package com.djamware.springbootmongodbsecurity.repository;

import com.djamware.springbootmongodbsecurity.domain.User;
import org.springframework.data.mongodb.repository.MongoRepository;

public interface UserRepository extends MongoRepository<User, String> {

    User findByEmail(String email);

}

In that repository simply declare a single query for getting user data by email. Next, do the same steps with previous for creating new Java Interface file with the name `RoleRepository` and the same package name `com.springauth.springsecurityauth.repository` then click Finish button. After the newly created Java Interface file opened automatically, replace all codes with this.

package com.djamware.springbootmongodbsecurity.repository;

import com.djamware.springbootmongodbsecurity.domain.Role;
import org.springframework.data.mongodb.repository.MongoRepository;

public interface RoleRepository extends MongoRepository<Role, String> {

    Role findByRole(String role);
}

In that repository simply declare a single query for getting role data by role name.


Create a Custom Java User Details Service

We need to create a service for User CRUD functions and implements the Spring Security User Details Service. On the Netbeans right-click the project name then click New then click Java Class. On the new Java Class form, fill Class Name with `CustomUserDetailsService` and package name `com.springauth.springsecurityauth.services` then click Finish button.

Spring Boot, Security, and Data MongoDB Authentication Example - Netbeans Customer Details Service Class

After the newly created Java Class file opened, add these imports of required Java Util, Spring Framework Security Core, and Annotation after the package name.

package com.djamware.springbootmongodbsecurity.service;

import com.djamware.springbootmongodbsecurity.domain.Role;
import com.djamware.springbootmongodbsecurity.domain.User;
import com.djamware.springbootmongodbsecurity.repository.RoleRepository;
import com.djamware.springbootmongodbsecurity.repository.UserRepository;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
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.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

To make this class as a Spring Boot service, add this annotation before the class name.

@Service
public class CustomUserDetailsService implements UserDetailsService {

}

Declare the user and role repositories and `BCryptPasswordEncoder` for the password encryption after the class name.

@Autowired
private UserRepository userRepository;
@Autowired
private RoleRepository roleRepository;
@Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;

Create a method for getting the user by email.

public User findUserByEmail(String email) {
    return userRepository.findByEmail(email);
}

Create a method for save a new user, encrypt the password and set a role for the user. For now, we will use the role `ADMIN` for all newly registered user.

public void saveUser(User user) {
    user.setPassword(bCryptPasswordEncoder.encode(user.getPassword()));
    user.setEnabled(true);
    Role userRole = roleRepository.findByRole("ADMIN");
    user.setRoles(new HashSet<>(Arrays.asList(userRole)));
    userRepository.save(user);
}

Create a method for handling the login mechanism that checks or compares username with the user from MongoDB collection.

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

    User user = userRepository.findByEmail(email);
    if(user != null) {
        List<GrantedAuthority> authorities = getUserAuthority(user.getRoles());
        return buildUserForAuthentication(user, authorities);
    } else {
        throw new UsernameNotFoundException("username not found");
    }
}

That method has a method for converting the user roles as GrantedAuthority collection. Create a new method like this.

private List<GrantedAuthority> getUserAuthority(Set<Role> userRoles) {
    Set<GrantedAuthority> roles = new HashSet<>();
    userRoles.forEach((role) -> {
        roles.add(new SimpleGrantedAuthority(role.getRole()));
    });

    List<GrantedAuthority> grantedAuthorities = new ArrayList<>(roles);
    return grantedAuthorities;
}

Finally, add the method for connecting MongoDB user to Spring Security user as called from the `loadUserByUsername` method.

private UserDetails buildUserForAuthentication(User user, List<GrantedAuthority> authorities) {
    return new org.springframework.security.core.userdetails.User(user.getEmail(), user.getPassword(), authorities);
}


Create Controller for All Spring MVC Views

To accessing or manage between views and data we have to create a controller. You may create each controller for each module, but right here we just create a single controller file for some views. On the Netbeans right-click the project name then click New then click Java Class. On the new Java Class form, fill Class Name with `LoginController` and package name `com.springauth.springsecurityauth.controller` then click the Finish button.

Spring Boot, Security, and Data MongoDB Authentication Example - Netbeans Login Controller

After the newly created Java Class file opened, add these imports of Spring Framework Security, Controller, web, and validation after the package name.

import com.djamware.springbootmongodbsecurity.domain.User;
import com.djamware.springbootmongodbsecurity.service.CustomUserDetailsService;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;

And add Spring MVC annotation before the class name.

@Controller
public class LoginController {

}

Declare the previously created `CustomUserDetailsService` after the class name.

@Autowired
private CustomUserDetailsService userService;

Create a model and view method for the login page.

@RequestMapping(value = "/login", method = RequestMethod.GET)
public ModelAndView login() {
    ModelAndView modelAndView = new ModelAndView();
    modelAndView.setViewName("login");
    return modelAndView;
}

Create a model and view method for signup/register page.

@RequestMapping(value = "/signup", method = RequestMethod.GET)
public ModelAndView signup() {
    ModelAndView modelAndView = new ModelAndView();
    User user = new User();
    modelAndView.addObject("user", user);
    modelAndView.setViewName("signup");
    return modelAndView;
}

Create a model and view method for saving the new user when form submitted from the signup page.

@RequestMapping(value = "/signup", method = RequestMethod.POST)
public ModelAndView createNewUser(@Valid User user, BindingResult bindingResult) {
    ModelAndView modelAndView = new ModelAndView();
    User userExists = userService.findUserByEmail(user.getEmail());
    if (userExists != null) {
        bindingResult
                .rejectValue("email", "error.user",
                        "There is already a user registered with the username provided");
    }
    if (bindingResult.hasErrors()) {
        modelAndView.setViewName("signup");
    } else {
        userService.saveUser(user);
        modelAndView.addObject("successMessage", "User has been registered successfully");
        modelAndView.addObject("user", new User());
        modelAndView.setViewName("login");

    }
    return modelAndView;
}

Create a model and view method for admin dashboard page which is a secure page that will show data from the successful login.

@RequestMapping(value = "/dashboard", method = RequestMethod.GET)
public ModelAndView dashboard() {
    ModelAndView modelAndView = new ModelAndView();
    Authentication auth = SecurityContextHolder.getContext().getAuthentication();
    User user = userService.findUserByEmail(auth.getName());
    modelAndView.addObject("currentUser", user);
    modelAndView.addObject("fullName", "Welcome " + user.getFullname());
    modelAndView.addObject("adminMessage", "Content Available Only for Users with Admin Role");
    modelAndView.setViewName("dashboard");
    return modelAndView;
}

Finally, create a model and view for the initial page that load in the front of the browser.

@RequestMapping(value = {"/","/home"}, method = RequestMethod.GET)
public ModelAndView home() {
    ModelAndView modelAndView = new ModelAndView();
    modelAndView.setViewName("home");
    return modelAndView;
}

You will need to create HTML files later for all of those views.


Add a Configuration for Spring MVC and Security

To make the view and controller available in the Spring Boot application, create a new file for a Spring Web MVC Configuration. On the Netbeans right-click the project name then click New then click Java Class. On the new Java Class form, fill Class Name with `PageConfig` and package name `com.springauth.springsecurityauth.config` then click the Finish button.

Spring Boot, Security, and Data MongoDB Authentication Example - Netbeans Page Config Class

After the newly created Java class file opened, add these imports after the package name.

package com.djamware.springbootmongodbsecurity.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

To make the class as a Spring Boot configuration class, add this annotation and implements Spring `WebMvcConfigurer` before the class name.

@Configuration
public class PageConfig implements WebMvcConfigurer {

}

Add BCryptPasswordEncoder bean.

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

Add an override method to register the controllers and the views.

@Override
public void addViewControllers(ViewControllerRegistry registry) {
    registry.addViewController("/home").setViewName("home");
    registry.addViewController("/").setViewName("home");
    registry.addViewController("/dashboard").setViewName("dashboard");
    registry.addViewController("/login").setViewName("login");
}

Do the same way to create Spring Web Security configuration using the file name `WebSecurityConfig` inside the same package name as the previous file. After the file opened, add these imports.

package com.djamware.springbootmongodbsecurity.config;

import com.djamware.springbootmongodbsecurity.service.CustomUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

To make the class as a Spring Boot configuration class, enable Spring Web Security and extends Spring Security `WebSecurityConfigurerAdapter`, add these annotations before the class name and extends after the class name.

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

}

Declare the variable for `BCryptPasswordEncoder` and `CustomizeAuthenticationSuccessHandler` class that will created later.

@Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;

@Autowired
CustomizeAuthenticationSuccessHandler customizeAuthenticationSuccessHandler;

Create a bean for the Spring Security `UserDetailsService` that use the `CustomUserDetailsService` class.

@Bean
public UserDetailsService mongoUserDetails() {
    return new CustomUserDetailsService();
}

Add an override method for a manage authentication mechanism that uses `UserDetailsService` and Bcrypt password encoder.

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    UserDetailsService userDetailsService = mongoUserDetails();
    auth
        .userDetailsService(userDetailsService)
        .passwordEncoder(bCryptPasswordEncoder);

}

Add an override method for securing the HTTP requests.

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
        .authorizeRequests()
            .antMatchers("/").permitAll()
            .antMatchers("/login").permitAll()
            .antMatchers("/signup").permitAll()
            .antMatchers("/dashboard/**").hasAuthority("ADMIN").anyRequest()
            .authenticated().and().csrf().disable().formLogin().successHandler(customizeAuthenticationSuccessHandler)
            .loginPage("/login").failureUrl("/login?error=true")
            .usernameParameter("email")
            .passwordParameter("password")
            .and().logout()
            .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
            .logoutSuccessUrl("/").and().exceptionHandling();
}

That configuration also adds custom login success handler using the custom class. Next, add an override method to exclude static resources that use in this web application. There also a custom username parameter that we use `email` as the parameter.

@Override
public void configure(WebSecurity web) throws Exception {
    web
        .ignoring()
        .antMatchers("/resources/**", "/static/**", "/css/**", "/js/**", "/images/**");
}

Finally, we need to create a class for the custom login success handler. Do the same way as the previous step using the file name `CustomizeAuthenticationSuccessHandler` with the same package. After file created and opened, add these imports.

package com.djamware.springbootmongodbsecurity.config;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;

Make this class as a Spring component and implements Spring Security `AuthenticationSuccessHandler` by adding this annotation and implements.

@Component
public class CustomizeAuthenticationSuccessHandler implements AuthenticationSuccessHandler {

}

Add an override method for custom landing page after successful login.

@Override
public void onAuthenticationSuccess(HttpServletRequest request,
        HttpServletResponse response, Authentication authentication)
        throws IOException, ServletException {
    //set our response to OK status
    response.setStatus(HttpServletResponse.SC_OK);

    for (GrantedAuthority auth : authentication.getAuthorities()) {
        if ("ADMIN".equals(auth.getAuthority())) {
            response.sendRedirect("/dashboard");
        }
    }
}


Create All Required Spring MVC Views with Bootstrap

To create HTML views that implements Thymeleaf library, first, we have to add a folder with the name `templates` inside `resources` folder. Then create HTML files inside that folder.

mkdir src/main/resources
touch src/main/resources/default.html
touch src/main/resources/home.html
touch src/main/resources/dashboard.html
touch src/main/resources/login.html
touch src/main/resources/signup.html

We are using Thymeleaf layout decoration which the base page is `default.html`. Open and edit that HTML file then replaces all HTML tags with this tags.

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
    <head>
        <title layout:title-pattern="$CONTENT_TITLE - $LAYOUT_TITLE">Default</title>
        <meta name="description" content=""/>
        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous" />
        <link rel="stylesheet" href="/css/style.css" />
    </head>
    <body>
        <nav class="navbar navbar-expand-lg navbar-dark bg-dark fixed-top">
            <a class="navbar-brand" href="/">Spring Boot</a>
            <button class="navbar-toggler" type="button" data-toggle="collapse" data-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 mr-auto">
                    <li class="nav-item active">
                        <a class="nav-link" href="/">Home <span class="sr-only">(current)</span></a>
                    </li>
                </ul>
                <ul class="navbar-nav ml-auto">
                    <li class="nav-item" th:if="${currentUser == null}"><a class="nav-link" th:href="@{/login}">Log in</a></li>
                    <li class="nav-item" th:if="${currentUser}">
                        <a class="nav-link" th:text="${fullName}"></a>
                        <form th:action="@{/logout}" method="post">
                            <input type="submit" value="Sign Out"/>
                        </form>
                    </li>
                </ul>
            </div>
        </nav>

        <div class="container">
            <div layout:fragment="content"></div>
        </div>
        <!-- /.container -->

        <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
        <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
    </body>
</html>

As you see, we are using Bootstrap 4 as the responsive frontend, all required CSS and Javascript library loaded from CDN. We put the condition in the top navigation bar for show login and log out button. The other pages put inside `layout:fragment` that will be using the same layout.

Next, open and edit `home.html` for starting page. Replace all HTML tags with this.

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{default}">
    <head>
        <title>Spring Security Example</title>
    </head>
    <body>
        <div layout:fragment="content">
            <h1>Welcome!</h1>

        <p>Click <a th:href="@{/hello}">here</a> to see a greeting.</p>
        </div>
    </body>
</html>

As you can see this Thymeleaf syntax `layout:decorate` using `default.html` as the layout template. There's nothing special here, just a static landing page.

Next, open and edit `dashboard.html` then replace all HTML tags with these tags.

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{default}">
    <head>
        <title>Dashboard</title>
    </head>
    <body>
        <div layout:fragment="content">
            <h1 th:inline="text">Hello [[${#httpServletRequest.remoteUser}]]!</h1>
            <h2>This is your Admin Dashboard</h2>
        </div>
    </body>
</html>

Next, open and edit `login.html` then replace all HTML tags with these tags.

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{default}">
    <head>
        <title>Login</title>
    </head>
    <body class="text-center">
        <div layout:fragment="content">
            <form class="form-signin" th:action="@{/login}" method="post">
                <img class="mb-4" src="https://getbootstrap.com/assets/brand/bootstrap-solid.svg" alt="" width="72" height="72" />
                <h1 class="h3 mb-3 font-weight-normal">Please sign in</h1>
                <div th:if="${param.error}">
                    Invalid email and password.
                </div>
                <div th:if="${param.logout}">
                    You have been logged out.
                </div>
                <label for="inputEmail" class="sr-only">Email address</label>
                <input type="email" name="email" id="inputEmail" class="form-control" placeholder="Email" required="" />
                <label for="inputPassword" class="sr-only">Password</label>
                <input type="password" name="password" id="inputPassword" class="form-control" placeholder="Password" required="" />
                <button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
            </form>
            <form class="form-signin" th:action="@{/signup}" method="get">
                <button class="btn btn-md btn-success btn-block" type="Submit">Signup Here</button>
            </form>
        </div>
    </body>
</html>

If you don't configure the username field in the Spring Web Security configuration you should use input name `username`.

Finally, open and edit `signup.html` then replace all HTML tags with these tags.

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{default}">
    <head>
        <title>Signup</title>
    </head>
    <body>
        <div layout:fragment="content">
            <form class="form-signin" th:action="@{/signup}" method="post">
                <img class="mb-4" src="https://getbootstrap.com/assets/brand/bootstrap-solid.svg" alt="" width="72" height="72" />
                <h1 class="h3 mb-3 font-weight-normal">Signup Here</h1>
                <div th:if="${param.error}">
                    Invalid email and password.
                </div>
                <div th:if="${param.logout}">
                    You have been logged out.
                </div>
                <label for="inputUsername" class="sr-only">Username</label>
                <input type="email" name="email" id="inputEmail" class="form-control" placeholder="Username" required="" />
                <label for="inputPassword" class="sr-only">Password</label>
                <input type="password" name="password" id="inputPassword" class="form-control" placeholder="Password" required="" />
                <label for="inputFullname" class="sr-only">Full Name</label>
                <input type="text" name="fullname" id="inputEmail" class="form-control" placeholder="Fullname" required="" />
                <button class="btn btn-lg btn-primary btn-block" type="submit">Sign Up</button>
            </form>
            <form class="form-signin" th:action="@{/login}" method="get">
                <button class="btn btn-md btn-success btn-block" type="Submit">Sign In</button>
            </form>
        </div>
    </body>
</html>

Before we test our Spring Boot application, add CSS file for custom style.

mkdir src/main/resources/static/css
touch src/main/resources/static/css/style.css

Open and edit `style.css` then add these lines of CSS codes.

html,
body {
  height: 100%;
}

body {
  display: -ms-flexbox;
  display: flex;
  -ms-flex-align: center;
  align-items: center;
  padding-top: 40px;
  padding-bottom: 40px;
  background-color: #f5f5f5;
}

.form-signin {
  width: 100%;
  max-width: 330px;
  padding: 15px;
  margin: auto;
}
.form-signin .checkbox {
  font-weight: 400;
}
.form-signin .form-control {
  position: relative;
  box-sizing: border-box;
  height: auto;
  padding: 10px;
  font-size: 16px;
}
.form-signin .form-control:focus {
  z-index: 2;
}
.form-signin input[type="email"] {
  margin-bottom: -1px;
  border-bottom-right-radius: 0;
  border-bottom-left-radius: 0;
}
.form-signin input[type="password"] {
  margin-bottom: 10px;
  border-top-left-radius: 0;
  border-top-right-radius: 0;
}

.form-signin input[type="text"] {
  margin-bottom: 10px;
}


Run and Test The Spring Boot Security and MongoDB Java Web Application

Before running the application, we need to add Role data. For that, open and edit `src/main/java/com/djamware/springbootmongodbsecurity/SpringbootMongodbSecurityApplication.java` then add/replace these imports.

import com.djamware.springbootmongodbsecurity.domain.Role;
import com.djamware.springbootmongodbsecurity.repository.RoleRepository;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

Add a bean after the main method.

@Bean
CommandLineRunner init(RoleRepository roleRepository) {

    return args -> {

        Role adminRole = roleRepository.findByRole("ADMIN");
        if (adminRole == null) {
            Role newAdminRole = new Role();
            newAdminRole.setRole("ADMIN");
            roleRepository.save(newAdminRole);
        }

        Role userRole = roleRepository.findByRole("USER");
        if (userRole == null) {
            Role newUserRole = new Role();
            newUserRole.setRole("USER");
            roleRepository.save(newUserRole);
        }
    };

}

Now, run the whole web application. Don't forget to run MongoDB daemon in another Terminal tab.

./gradlew bootRun

Open the browser then point to `localhost:8080` and you should see this page. Then you can try login or signup function.

Spring Boot, Security, and Data MongoDB Authentication Example - Welcome Page
Spring Boot, Security, and Data MongoDB Authentication Example - Login Page
Spring Boot, Security, and Data MongoDB Authentication Example - Signup Page
Spring Boot, Security, and Data MongoDB Authentication Example - Ready to Login
Spring Boot, Security, and Data MongoDB Authentication Example - Admin Dashboard Page

That it's, the comprehensive Spring Boot Security and MongoDB using custom User Details Service. If you can't follow these steps, you can compare with the working source on our GitHub and the updated version.

That just the basic. If you need more deep learning about Java and Spring Framework you can take the following cheap course:

Thanks!