Last updated on March 9th, 2024
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
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
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.