Spring Boot 2 - simple custom authentication example using JPA and MySQL database
In this article, we would like to show how to create a simple web application with custom authentication using Spring Boot 2, JPA, and MySQL database.
Notes:
In the below example:
- to generate construstors, getters and setters automatically Lombok pre-procesor was used
(check request, response and entity classes),- to generate query methods automatically and query database Spring Data JPA was used
(checkUsersRepository
interface).
Final effect:

Project structure:
DemoApplication.java
file:
xxxxxxxxxx
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
AuthenticationController.java
file:
xxxxxxxxxx
package com.example.demo;
import com.example.demo.requests.UserLoginRequest;
import com.example.demo.requests.UserRegisterRequest;
import com.example.demo.responses.UserActionResponse;
import com.example.demo.responses.UserCheckResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import javax.servlet.http.HttpServletRequest;
public class AuthenticationController {
private AuthenticationService authenticationService;
"/api/user/check") (
public ResponseEntity<UserCheckResponse> checkUser(HttpServletRequest request) {
return ResponseEntity.ok(this.authenticationService.checkUser(request));
}
"/api/user/login") (
public ResponseEntity<UserActionResponse> loginUser(HttpServletRequest request, UserLoginRequest data) {
return ResponseEntity.ok(this.authenticationService.loginUser(request, data));
}
"/api/user/logout") (
public ResponseEntity<UserActionResponse> logoutUser(HttpServletRequest request) {
return ResponseEntity.ok(this.authenticationService.logoutUser(request));
}
"/api/user/register") (
public ResponseEntity<UserActionResponse> registerUser( UserRegisterRequest data) {
return ResponseEntity.ok(this.authenticationService.registerUser(data));
}
}
AuthenticationService.java
file:
xxxxxxxxxx
package com.example.demo;
import com.example.demo.entities.UserEntity;
import com.example.demo.repositories.UsersRepository;
import com.example.demo.requests.UserLoginRequest;
import com.example.demo.requests.UserRegisterRequest;
import com.example.demo.responses.UserActionResponse;
import com.example.demo.responses.UserCheckResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.Optional;
public class AuthenticationService {
private UsersRepository usersRepository;
public boolean isUserLogged(HttpSession session) {
Long loggedUserId = this.getLoggedUserId(session);
return loggedUserId != null;
}
public boolean isUserLogged(HttpServletRequest request) {
return this.isUserLogged(request.getSession());
}
public Long getLoggedUserId(HttpSession session) {
return (Long) session.getAttribute("LOGGED_USER_ID");
}
public Long getLoggedUserId(HttpServletRequest request) {
return this.getLoggedUserId(request.getSession());
}
public UserCheckResponse checkUser(HttpServletRequest request) {
Long loggedUserId = this.getLoggedUserId(request);
if (loggedUserId == null) {
return new UserCheckResponse(false, "Currently, You are not logged in.", null);
}
Optional<UserEntity> userOptional = this.usersRepository.findById(loggedUserId);
if (userOptional.isPresent()) {
UserEntity userEntity = userOptional.get();
return new UserCheckResponse(true, "Currently, You are logged in.", userEntity);
} else {
return new UserCheckResponse(false, "Logged in user doesn't exists.", null);
}
}
public UserActionResponse loginUser(HttpServletRequest request, UserLoginRequest data) {
HttpSession session = request.getSession();
if (this.isUserLogged(session)) {
return new UserActionResponse(false, "Login operation failed (You are already logged in).");
}
Optional<UserEntity> userOptional = this.usersRepository.findByUsernameAndPassword(data.getUsername(), data.getPassword());
if (userOptional.isPresent()) {
UserEntity userEntity = userOptional.get();
session.setAttribute("LOGGED_USER_ID", userEntity.getId());
return new UserActionResponse(true, "Login operation succeed.");
}
return new UserActionResponse(false, "Login operation failed (Incorrect user credentials).");
}
public UserActionResponse logoutUser(HttpServletRequest request) {
HttpSession session = request.getSession();
if (this.isUserLogged(session)) {
session.removeAttribute("LOGGED_USER_ID");
return new UserActionResponse(true, "Logout operation succeed.");
}
return new UserActionResponse(false, "Logout operation failed (Currently, You are not logged in).");
}
public UserActionResponse registerUser(UserRegisterRequest data) {
UserEntity userEntity = new UserEntity(null, data.getUsername(), data.getPassword(), data.getEmail());
try {
this.usersRepository.save(userEntity);
return new UserActionResponse(true, "Register operation succeed.");
} catch (Throwable ex) {
if (ex instanceof DataIntegrityViolationException) {
return new UserActionResponse(false, "Register operation failed (username or email is used already by someone).");
}
return new UserActionResponse(false, "Register operation failed.");
}
}
}
repositories/UsersRepository.java
file:
xxxxxxxxxx
package com.example.demo.repositories;
import com.example.demo.entities.UserEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.Optional;
public interface UsersRepository extends JpaRepository<UserEntity, Long> {
Optional<UserEntity> findByUsernameAndPassword(String username, String password);
}
entities/UserEntity.java
file:
xxxxxxxxxx
package com.example.demo.entities;
import lombok.Getter;
import lombok.Setter;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import javax.persistence.*;
name = "users") (
public class UserEntity {
strategy = GenerationType.IDENTITY) (
private Long id;
name = "username", nullable = false, unique = true) (
private String username;
name = "password", nullable = false) (
private String password;
name = "email", nullable = false, unique = true) (
private String email;
}
requests/UserLoginRequest.java
file:
xxxxxxxxxx
package com.example.demo.requests;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
public class UserLoginRequest {
private String username;
private String password;
}
requests/UserRegisterRequest.java
file:
xxxxxxxxxx
package com.example.demo.requests;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
public class UserRegisterRequest {
private String username;
private String password;
private String email;
}
responses/UserActionResponse.java
file:
xxxxxxxxxx
package com.example.demo.responses;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
public class UserActionResponse {
private boolean success;
private String message;
}
responses/UserCheckResponse.java
file:
xxxxxxxxxx
package com.example.demo.responses;
import com.example.demo.entities.UserEntity;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
public class UserCheckResponse {
private boolean success;
private String message;
private UserEntity user;
}
resources/static/index.html
file:
xxxxxxxxxx
<html>
<head>
<style>
th {
padding: 6px;
}
td {
padding: 6px;
border: 1px solid silver;
}
</style>
</head>
<body>
<button onclick="checkUser()">Check user</button>
<br /><br />
<table>
<tr>
<th>Username</th>
<th>Password</th>
<th>E-mail</th>
<th colspan="2"></th>
</tr>
<tr>
<td>admin</td>
<td>admin</td>
<td>admin@email.com</td>
<td><button onclick="loginUser('admin', 'admin')">Login</button></td>
<td><button onclick="registerUser('admin', 'admin', 'admin@email.com')">Register</button></td>
</tr>
<tr>
<td>user</td>
<td>user</td>
<td>user@email.com</td>
<td><button onclick="loginUser('user', 'user')">Login</button></td>
<td><button onclick="registerUser('user', 'user', 'user@email.com')">Register</button></td>
</tr>
</table>
<br />
<button onclick="logoutUser()">Logout user</button>
<script>
// Reusable logic:
const sendData = async (requestUrl, requestData) => {
const config = {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
};
if (requestData) {
config.body = JSON.stringify(requestData); // request body
}
const response = await fetch(requestUrl, config);
return await response.json();
};
// Example actions:
const checkUser = async () => {
const responseData = await sendData('/api/user/check');
alert(JSON.stringify(responseData, null, 4));
};
const loginUser = async (username, password) => {
const requestData = {username, password};
const responseData = await sendData('/api/user/login', requestData);
alert(JSON.stringify(responseData, null, 4));
};
const logoutUser = async () => {
const responseData = await sendData('/api/user/logout');
alert(JSON.stringify(responseData, null, 4));
};
const registerUser = async (username, password, email) => {
const requestData = {username, password, email};
const responseData = await sendData('/api/user/register', requestData);
alert(JSON.stringify(responseData, null, 4));
};
</script>
</body>
</html>
resources/application.properties
file:
xxxxxxxxxx
db.host=localhost
db.port=3306
db.name=demo_db
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://${db.host}:${db.port}/${db.name}?useUnicode=yes&characterEncoding=UTF-8&serverTimezone=UTC&character_set_server=utf8mb4
spring.datasource.username=root
spring.datasource.password=root
spring.jpa.hibernate.ddl-auto=update
Hint: to run database in the computer memory go to this snippet to see how to use H2 database.
pom.xml
file:
xxxxxxxxxx
<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.6.5</version>
<relativePath/>
</parent>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<java.version>11</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.22</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Hint: to run database in the computer memory go to this snippet to see how to use H2 database (in that case
mysql-connector-java
dependency can be removed frompom.xml
file).
1. Database should be created using:
xxxxxxxxxx
CREATE DATABASE `demo_db`
2. users
table will be created or updated automatically by Spring Boot 2 Application
(configured by spring.jpa.hibernate.ddl-auto=update
property).