Registration, Login and Logout Web Application – Spring Security

In this example, we will learn how to implement registration, login web application and logout using Spring boot, spring security, hibernate, H2 database and thymeleaf.
We will create an example of a Spring Boot web application where we will create three view pages(index, login and registration), three functionalities (registration, login and logout), two entity classes, two repositories interfaces and two DTO classes.

Table of Content

1. Keep Eclipse IDE ready(STS integrated)
2. Create a Spring Boot Starter Project 
3. Maven Dependency
4. Define Database configuration in the application.properties file
5. Create entity class
6. Create a repository
7. Create a service
8. Create a DTO class
9. Create a Controller class
10. Create a view template
11. Run the Application
12. Conclusion

1. Keep Eclipse IDE ready(STS integrated)

Refer to this article How to Create Spring Project in IDE to create Spring Boot Project in Eclipse IDE.

2. Create a Spring Boot Starter Project 

Add the following dependencies: 
• Spring Web 
• Spring Security
• H2 Database
• Spring Data JPA
• Thymeleaf

Project Structure of Registration, Login Web Application and Logout

registration_login_and_logout_web_application_using_spring_security

3. Maven Dependency

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.11</version>
        <packaging>war</packaging>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.springjava</groupId>
    <artifactId>Registration_Login_Logout_With_Spring_Security</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>Registration_Login_Logout_With_Spring_Security</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>16</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>  
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
   </build>
</project>

4. Define Database configuration in the application.properties file

spring.datasource.url=jdbc:h2:mem:test
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=update
spring.h2.console.enabled=true

→ In this file we are configuring the H2 database to this application.

5. Create entity class

Role.java

package com.springjava.entity;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="roles")
public class Role {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private String name;
    public Role() {

    }
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

User.java

package com.springjava.entity;
import java.util.Collection;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
import org.hibernate.annotations.LazyCollection;
import org.hibernate.annotations.LazyCollectionOption;

@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private String firstName;
    private String lastName;
    private String email;
    private String password;
    @ManyToMany(cascade=CascadeType.ALL)
    @LazyCollection(LazyCollectionOption.FALSE)
    private Collection<Role> roles;
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getFirstName() {
        return firstName;
    }
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
    public String getLastName() {
        return lastName;
    }
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
    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 Collection<Role> getRoles() {
        return roles;
    }
    public void setRoles(Collection<Role> roles) {
        this.roles = roles;
    }
}

6. Create a repository

RoleRepository.java

package com.springjava.repository;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import com.springjava.entity.Role;
public interface RoleRepository extends JpaRepository<Role, Integer> {
Optional<Role> findByName(String name);
}

UserRepository.java

package com.springjava.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import com.springjava.entity.User;
public interface UserRepository extends JpaRepository<User, Integer> {
User findByEmail(String email);
}

7. Create a service

UserService.java

package com.springjava.service;
import org.springframework.security.core.userdetails.UserDetailsService;
import com.springjava.dto.UserRegisterDto;
import com.springjava.entity.User;
public interface UserService extends UserDetailsService {
User save(UserRegisterDto userRegDto);
}

UserServiceImpl.java

package com.springjava.service;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
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.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import com.springjava.dto.UserRegisterDto;
import com.springjava.entity.Role;
import com.springjava.entity.User;
import com.springjava.repository.UserRepository;
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserRepository userRepo;
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepo.findByEmail(username);
if (user == null){
throw new UsernameNotFoundException("Invalid username or password.");
}
return new org.springframework.security.core.userdetails.User(user.getEmail(), user.getPassword(), mapRolesToAuthorities(user.getRoles()));
}
@Override
public User save(UserRegisterDto userRegDto) {
User user = new User();
user.setFirstName(userRegDto.getFirstName());
user.setLastName(userRegDto.getLastName());
user.setEmail(userRegDto.getEmail());
user.setPassword(passwordEncoder.encode(userRegDto.getPassword()));
List<Role> roles=new ArrayList<Role>();
Role role=new Role();
role.setName("ROLE_USER");
roles.add(role);
user.setRoles(roles);
return userRepo.save(user);
}
private Collection < ? extends GrantedAuthority > mapRolesToAuthorities(Collection < Role > roles) {
return roles.stream().map(role-> new SimpleGrantedAuthority(role.getName())).collect(Collectors.toList());
    }
}

8. Create a DTO class

LoginDto.java

package com.springjava.dto;
public class LoginDto {
private String username;
private String password;
public LoginDto() {
}
public String getUsername(){
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
    }
}

UserRegisterDto.java

package com.springjava.dto;
public class UserRegisterDto {
private String firstName;
private String lastName;
private String email;
private String password;
public UserRegisterDto(){
}
public String getFirstName(){
return firstName;
}
public void setFirstName(String firstName){
this.firstName = firstName;
}
public String getLastName(){
return lastName;
}
public void setLastName(String lastName){
this.lastName = lastName;
}
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;
  }
}

9. Create a Controller class

HomeController.java

package com.springjava.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class HomeController {
@GetMapping("/login")
public String login(){
return "login";
}
@GetMapping("/")
public String home(){
return "index";
    }
}

UserRegistrationController.java

package com.springjava.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import com.springjava.dto.UserRegisterDto;
import com.springjava.service.UserService;
@Controller
@RequestMapping("/registration")
public class UserRegistrationController {
@Autowired
private UserService userService;
@ModelAttribute("user")
public UserRegisterDto userRegistrationDto(){
return new UserRegisterDto();
}
@GetMapping
public String showRegistrationForm() {
return "registration";
}
@PostMapping
public String registerUserAccount(@ModelAttribute("user") UserRegisterDto registrationDto) {
userService.save(registrationDto);
return "redirect:/registration?success";
    }
}

10. Create a Spring Security Config class

SecurityConfig.java

package com.springjava.security;
import org.springframework.beans.factory.annotation.Autowired;
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 org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import com.springjava.service.UserService;
@Configuration
public class SecurityConfig {
@Autowired
private UserService userService;

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

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

@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/registration**").permitAll()
         .requestMatchers(new AntPathRequestMatcher("/h2-console/**"))
         .permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/login")
                .permitAll()
                .and()
                .logout()
                .invalidateHttpSession(true)
                .clearAuthentication(true)
                .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
                .logoutSuccessUrl("/login?logout")
                .permitAll();
             http.csrf().disable();
            http.headers().frameOptions().disable();
return http.build();
}
    
@Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider auth = new DaoAuthenticationProvider();
auth.setUserDetailsService(userService);
auth.setPasswordEncoder(passwordEncoder());
return auth;
     }
}

→ In the above class configure Spring Security in this application.
→ DaoAuthenticationProvider class is a simple authentication provider that uses DAO to fetch user detail from the database.
→ In this class we are permitted to access to H2 Database console.

10. Create a view template

Create these three view template pages under main/src/resources/template

index.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
 xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
<meta charset="ISO-8859-1">
<title>Registration and Login</title>
<link rel="stylesheet"
 href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
</head>
<body>
 <!-- create navigation bar ( header) -->
 <nav class="navbar navbar-inverse navbar-fixed-top">
  <div class="container">
   <div class="navbar-header">
    <button type="button" class="navbar-toggle collapsed"
     data-toggle="collapse" data-target="#navbar" aria-expanded="false"
     aria-controls="navbar">
     <span class="sr-only">Toggle navigation</span> <span
      class="icon-bar"></span> <span class="icon-bar"></span> <span
      class="icon-bar"></span>
    </button>
    <a class="navbar-brand" href="#" th:href="@{/}">Registration and
     Login</a>
   </div>
   <div id="navbar" class="collapse navbar-collapse">
    <ul class="nav navbar-nav">
     <li sec:authorize="isAuthenticated()"><a th:href="@{/logout}">Logout</a></li>
    </ul>
   </div>
  </div>
 </nav>
<br>
<br>
<div class="container">
<h1>Home</h1>
Welcome <span sec:authentication="principal.username"> User</span>
</div>
</body>
</html>

login.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
 xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
<meta charset="ISO-8859-1">
<title>Registration and Login</title>
<link rel="stylesheet"
 href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
</head>
<body>
 <!-- create navigation bar ( header) -->
 <nav class="navbar navbar-inverse navbar-fixed-top">
  <div class="container">
   <div class="navbar-header">
    <button type="button" class="navbar-toggle collapsed"
     data-toggle="collapse" data-target="#navbar" aria-expanded="false"
     aria-controls="navbar">
     <span class="sr-only">Toggle navigation</span> <span
      class="icon-bar"></span> <span class="icon-bar"></span> <span
      class="icon-bar"></span>
    </button>
    <a class="navbar-brand" href="#" th:href="@{/}">Registration and
     Login</a>
   </div>
  </div>
 </nav>
 <br>
 <br>
 <div class = "container">
  <div class = "row">
   <div class = "col-md-6 col-md-offset-3">
    <h1> User Login Page </h1>
    <form th:action="@{/login}" method="post">
     <!-- error message -->
     <div th:if="${param.error}">
      <div class="alert alert-danger">Invalid username or
       password.</div>
     </div>
     <!-- logout message -->
     <div th:if="${param.logout}">
      <div class="alert alert-info">You have been logged out.</div>
     </div>
     <div class = "form-group">
      <label for ="username"> Username </label> :
      <input type="text" class = "form-control" id ="username" name = "username"
      placeholder="Enter Email ID" autofocus="autofocus">
     </div>
     <div class="form-group">
      <label for="password">Password</label>: <input type="password"
       id="password" name="password" class="form-control"
       placeholder="Enter Password" />
     </div>
     <div class="form-group">
      <div class="row">
       <div class="col-sm-6 col-sm-offset-3">
        <input type="submit" name="login-submit" id="login-submit"
         class="form-control btn btn-primary" value="Log In" />
       </div>
      </div>
     </div>
    </form>
    <div class="form-group">
      <span>New user? <a href="/" th:href="@{/registration}">Register
        here</a></span>
    </div>
   </div>
  </div>
 </div>
</body>
</html>

registration.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
 xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
<meta charset="ISO-8859-1">
<title>Registration and Login </title>
<link rel="stylesheet"
 href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
</head>
<body>
 <!-- create navigation bar ( header) -->
 <nav class="navbar navbar-inverse navbar-fixed-top">
  <div class="container">
   <div class="navbar-header">
    <button type="button" class="navbar-toggle collapsed"
     data-toggle="collapse" data-target="#navbar" aria-expanded="false"
     aria-controls="navbar">
     <span class="sr-only">Toggle navigation</span> <span
      class="icon-bar"></span> <span class="icon-bar"></span> <span
      class="icon-bar"></span>
    </button>
    <a class="navbar-brand" href="#" th:href="@{/}">Registration and
     Login</a>
   </div>
  </div>
 </nav>
<br>
<br>
 <!-- Create HTML registration form -->
 <div class="container">
  <div class="row">
   <div class="col-md-6 col-md-offset-3">
    <!-- success message -->
    <div th:if="${param.success}">
     <div class="alert alert-info">You've successfully registered</div>
    </div>
    <h1>Registration</h1>
    <form th:action="@{/registration}" method="post" th:object="${user}">
     <div class="form-group">
      <label class="control-label" for="firstName"> First Name </label>
      <input id="firstName" class="form-control" th:field="*{firstName}"
       required autofocus="autofocus" />
     </div>
     <div class="form-group">
      <label class="control-label" for="lastName"> Last Name </label> <input
       id="lastName" class="form-control" th:field="*{lastName}"
       required autofocus="autofocus" />
     </div>
     <div class="form-group">
      <label class="control-label" for="email"> Email </label> <input
       id="email" class="form-control" th:field="*{email}" required
       autofocus="autofocus" />
     </div>
     <div class="form-group">
      <label class="control-label" for="password"> Password </label> <input
       id="password" class="form-control" type="password"
       th:field="*{password}" required autofocus="autofocus" />
     </div>
     <div class="form-group">
      <button type="submit" class="btn btn-success">Register</button>
      <span>Already registered? <a href="/" th:href="@{/login}">Login
        here</a></span>
     </div>
    </form>
   </div>
  </div>
 </div>
</body>
</html>

11. Run the Application

To run this application right-click on the project and click on Run As then select the Run on Server option.
Output

registration_login_and_logout_web_application_using_spring_security


To Check the H2 Database browse this URL http://localhost:8088/Spring-Boot-Spring-Security/h2-console. Mention your Tomcat server port of the system. In my system, the Tomcat server runs on port number 8088.


12. Conclusion

In this example, we learnt how to implement registration, login and logout of a web application using Spring Boot, Spring Security, Hibernate, H2 Database and Thymeleaf.

Leave a Comment