Spring Boot Registration and Login with MySQL Database Tutorial
Spring Boot Registration and Login with MySQL Database Tutorial
- Details
- Written by Nam Ha Minh
- Last Updated on 28 October 2022 | Print Email
- Spring Web MVC for the web layer
- Spring Data JPA with Hibernate framework or the data access layer
- Spring Security for authentication, login and logout
- Thymeleaf as template engine
- HTML 5 and Bootstrap 4 for responsive user interface
- JUnit 5 and AssertJ for unit testing
- MySQL database
- Java Development Kit (JDK)
- Spring Tool Suite IDE (STS)
- MySQL Community server and MySQL Workbench
1. Create Spring Boot Project and Configure Dependencies
In Spring Tool Suite, create a new Spring Starter project with type Maven and language Java. And choose these dependencies: Spring Web, Thymeleaf, Spring Data JPA, MySQL Driver, Spring Security and Spring Boot DevTools – so the XML code for these dependencies in the pom.xml file is as follows:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</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-data-jpa</artifactId></dependency><dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope></dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId></dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional></dependency> |
1 2 3 4 5 6 7 8 9 10 11 | <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions></dependency> |
2. Create Database and Configure Data Source
Use MySQL Workbench or MySQL Command Line Client program to create a new database named codejavadb(you can choose any name you want):1 | create database codejavadb; |
1 2 3 4 5 | spring.jpa.hibernate.ddl-auto=createspring.datasource.url=jdbc:mysql://localhost:3306/codejavadbspring.datasource.username=rootspring.datasource.password=passwordspring.jpa.properties.hibernate.format_sql=true |
3. Code Entity Class and Repository Interface
Next, create a new Java class named User to map with the corresponding users table (not yet created) in the database, with the following code:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | package net.codejava;import javax.persistence.*;@Entity@Table(name = "users")public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false, unique = true, length = 45) private String email; @Column(nullable = false, length = 64) private String password; @Column(name = "first_name", nullable = false, length = 20) private String firstName; @Column(name = "last_name", nullable = false, length = 20) private String lastName; // getters and setters are not shown } |
1 2 3 4 5 6 7 | package net.codejava;import org.springframework.data.jpa.repository.JpaRepository;public interface UserRepository extends JpaRepository<User, Long> {} |
4. Code and Run Unit Test
Next, code a test class named UserRepositoryTests under src/test/java directory with the following skeleton code:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | package net.codejava;import static org.assertj.core.api.Assertions.assertThat;import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase.Replace;import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;import org.springframework.test.annotation.Rollback;@DataJpaTest@AutoConfigureTestDatabase(replace = Replace.NONE)@Rollback(false)public class UserRepositoryTests { @Autowired private TestEntityManager entityManager; @Autowired private UserRepository repo; // test methods go below} |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | @Testpublic void testCreateUser() { User user = new User(); user.setEmail("ravikumar@gmail.com"); user.setPassword("ravi2020"); user.setFirstName("Ravi"); user.setLastName("Kumar"); User savedUser = repo.save(user); User existUser = entityManager.find(User.class, savedUser.getId()); assertThat(user.getEmail()).isEqualTo(existUser.getEmail()); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | Hibernate: create table users ( id bigint not null auto_increment, email varchar(45) not null, first_name varchar(20) not null, last_name varchar(20) not null, password varchar(64) not null, primary key (id) ) engine=InnoDBHibernate: alter table users add constraint UK_6dotkott2kjsp8vw4d0m25fb7 unique (email) Hibernate: insert into users (email, first_name, last_name, password) values (?, ?, ?, ?) |
1 | Committed transaction for test: [DefaultTestContext@311bf055 testClass = UserRepositoryTests… |
1 | spring.jpa.hibernate.ddl-auto=none |
5. Code Controller class and Home Page
Next, let’s create a Spring MVC controller class named AppController, with the first handler method to show the home page, as follows:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | package net.codejava;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.GetMapping;@Controllerpublic class AppController { @Autowired private UserRepository userRepo; @GetMapping("") public String viewHomePage() { return "index"; }} |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <!DOCTYPE html><html xmlns:th="http://www.thymeleaf.org"><head> <meta charset="ISO-8859-1"> <title>Welcome to CodeJava Home</title> <link rel="stylesheet" type="text/css" href="/webjars/bootstrap/css/bootstrap.min.css" /> <script type="text/javascript" src="/webjars/jquery/jquery.min.js"></script> <script type="text/javascript" src="/webjars/bootstrap/js/bootstrap.min.js"></script></head><body> <div class="container text-center"> <h1>Welcome to CodeJava.net</h1> <h3><a th:href="/@{/users}">List of Users</a></h3> <h3><a th:href="/@{/register}">Register</a></h3> <h3><a th:href="/@{/login}">Login</a></h3> </div> </body></html> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <dependency> <groupId>org.webjars</groupId> <artifactId>jquery</artifactId> <version>3.4.1</version></dependency><dependency> <groupId>org.webjars</groupId> <artifactId>bootstrap</artifactId> <version>4.3.1</version></dependency><dependency> <groupId>org.webjars</groupId> <artifactId>webjars-locator-core</artifactId></dependency> |
You see, the home page shows 3 links List of Users, Register and Login. You will learn how to implement each function in the next few minutes. 6. Implement User Registration feature
Add a new handler method in the controller class to show the user registration form (sign up), with the following code:1 2 3 4 5 6 | @GetMapping("/register")public String showRegistrationForm(Model model) { model.addAttribute("user", new User()); return "signup_form";} |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | <!DOCTYPE html><html xmlns:th="http://www.thymeleaf.org"><head> <meta charset="ISO-8859-1"> <title>Sign Up - CodeJava</title> <link rel="stylesheet" type="text/css" href="/webjars/bootstrap/css/bootstrap.min.css" /> <script type="text/javascript" src="/webjars/jquery/jquery.min.js"></script> <script type="text/javascript" src="/webjars/bootstrap/js/bootstrap.min.js"></script></head><body> <div class="container text-center"> <div> <h1>User Registration - Sign Up</h1> </div> <form th:action="@{/process_register}" th:object="${user}" method="post" style="max-width: 600px; margin: 0 auto;"> <div class="m-3"> <div class="form-group row"> <label class="col-4 col-form-label">E-mail: </label> <div class="col-8"> <input type="email" th:field="*{email}" class="form-control" required /> </div> </div> <div class="form-group row"> <label class="col-4 col-form-label">Password: </label> <div class="col-8"> <input type="password" th:field="*{password}" class="form-control" required minlength="6" maxlength="10"/> </div> </div> <div class="form-group row"> <label class="col-4 col-form-label">First Name: </label> <div class="col-8"> <input type="text" th:field="*{firstName}" class="form-control" required minlength="2" maxlength="20"/> </div> </div> <div class="form-group row"> <label class="col-4 col-form-label">Last Name: </label> <div class="col-8"> <input type="text" th:field="*{lastName}" class="form-control" required minlength="2" maxlength="20" /> </div> </div> <div> <button type="submit" class="btn btn-primary">Sign Up</button> </div> </div> </form> </div></body></html> |
It looks very nice, isn’t it? Thanks to Bootstrap and HTML 5. You can also notice with HTML 5, the browser provides validation for input fields so you don’t have to use Javascript for that.Next, code a handler method in the controller class to process registration with the following code:1 2 3 4 5 6 7 8 9 10 | @PostMapping("/process_register")public String processRegister(User user) { BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); String encodedPassword = passwordEncoder.encode(user.getPassword()); user.setPassword(encodedPassword); userRepo.save(user); return "register_success";} |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <!DOCTYPE html><html xmlns:th="http://www.thymeleaf.org"><head> <meta charset="ISO-8859-1"> <title>Registration Success</title> <link rel="stylesheet" type="text/css" href="/webjars/bootstrap/css/bootstrap.min.css" /></head><body> <div class="container text-center"> <h3>You have signed up successfully!</h3> <h4><a th:href="/@{/login}">Click here to Login</a></h4> </div> </body></html> |
Now you can test the user registration feature and verify result in the database (note that the password should be encoded). 7. Code Custom UserDetails and UserDetailsService Classes
Next, in order to implement authentication (login) feature, we need to create a class of subtype UserDetails (defined by Spring Security) to represent an authentication user, with the following code:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | package net.codejava;import java.util.Collection;import org.springframework.security.core.GrantedAuthority;import org.springframework.security.core.userdetails.UserDetails;public class CustomUserDetails implements UserDetails { private User user; public CustomUserDetails(User user) { this.user = user; } @Override public Collection<? extends GrantedAuthority> getAuthorities() { return null; } @Override public String getPassword() { return user.getPassword(); } @Override public String getUsername() { return user.getEmail(); } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; } public String getFullName() { return user.getFirstName() + " " + user.getLastName(); }} |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | package net.codejava;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.security.core.userdetails.UserDetails;import org.springframework.security.core.userdetails.UserDetailsService;import org.springframework.security.core.userdetails.UsernameNotFoundException;public class CustomUserDetailsService implements UserDetailsService { @Autowired private UserRepository userRepo; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { User user = userRepo.findByEmail(username); if (user == null) { throw new UsernameNotFoundException("User not found"); } return new CustomUserDetails(user); }} |
1 2 3 4 5 | public interface UserRepository extends JpaRepository<User, Long> { @Query("SELECT u FROM User u WHERE u.email = ?1") public User findByEmail(String email); } |
8. Configure Spring Security for Authentication (Login)
Next, create a new Java class for configuring Spring Security with the following code:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | package net.codejava;import javax.sql.DataSource;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.security.authentication.dao.DaoAuthenticationProvider;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.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;@Configuration@EnableWebSecuritypublic class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private DataSource dataSource; @Bean public UserDetailsService userDetailsService() { return new CustomUserDetailsService(); } @Bean public BCryptPasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean public DaoAuthenticationProvider authenticationProvider() { DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider(); authProvider.setUserDetailsService(userDetailsService()); authProvider.setPasswordEncoder(passwordEncoder()); return authProvider; } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.authenticationProvider(authenticationProvider()); } @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/users").authenticated() .anyRequest().permitAll() .and() .formLogin() .usernameParameter("email") .defaultSuccessUrl("/users") .permitAll() .and() .logout().logoutSuccessUrl("/").permitAll(); } } |
Enter the username (email) and password of the user you have registered previously and click Sign in. You should see an error page because the list users page has not been implemented. 9. Code List Users Page and Logout
Next, we’re going to implement the list users and logout features. Update the controller class to have the following handler method:1 2 3 4 5 6 7 | @GetMapping("/users")public String listUsers(Model model) { List<User> listUsers = userRepo.findAll(); model.addAttribute("listUsers", listUsers); return "users";} |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | <!DOCTYPE html><html xmlns:th="http://www.thymeleaf.org"><head> <meta charset="ISO-8859-1"> <title>List Users</title> <link rel="stylesheet" type="text/css" href="/webjars/bootstrap/css/bootstrap.min.css" /> <script type="text/javascript" src="/webjars/jquery/jquery.min.js"></script> <script type="text/javascript" src="/webjars/bootstrap/js/bootstrap.min.js"></script></head><body><div class="container text-center"> <div> <form th:action="@{/logout}" method="post"> <p> Welcome <b>[[${#request.userPrincipal.principal.fullName}]]</b> </p> <input type="submit" value="Sign Out" /> </form> </div> <div> <h1>List of Users</h1> </div> <div> <table class="table table-striped table-bordered"> <thead class="thead-dark"> <tr> <th>User ID</th> <th>E-mail</th> <th>First Name</th> <th>Last Name</th> </tr> </thead> <tbody> <tr th:each="user: ${listUsers}"> <td th:text="${user.id}">User ID</td> <td th:text="${user.email}">E-mail</td> <td th:text="${user.firstName}">First Name</td> <td th:text="${user.lastName}">Last Name</td> </tr> </tbody> </table> </div></div></body></html> |
Let test adding more users and you will the list will contain more items. Click the Sign Out button, the application will log the user out and show the homepage.That’s the complete tutorial for implementing user registration and login features in a Spring Boot application with Spring Data JPA, Spring Security, MySQL database, Thymeleaf, Bootstrap and HTML 5. I hope you find this tutorial as good reference for your development need.What’s next? You may find this tutorial helpful: Spring Boot Email Verification for User Registration TutorialThe see to coding in action, I recommend you to watch the following video:Related Spring Security Tutorials:
- Spring Security Remember Me (Remember Login) Examples
- Spring Security Role-based Authorization Tutorial
- Spring Security Customize Login and Logout
- How to Get Logged-in User’s Details with Spring Security
- Spring Security: Prevent User from Going Back to Login Page if Already logged in
Other Spring Boot Tutorials:
- How to create a Spring Boot Web Application (Spring MVC with JSP/ThymeLeaf)
- Spring Boot CRUD Example with Spring MVC – Spring Data JPA – ThymeLeaf – Hibernate – MySQL
- Spring Boot Hello World RESTful Web Services Tutorial
- Spring Boot Thymeleaf Form Handling Tutorial
- Spring Data JPA Paging and Sorting Examples
- Spring Boot Error Handling Guide
- Spring Boot Logging Basics
About the Author:
Nam Ha Minh is certified Java programmer (SCJP and SCWCD). He started programming with Java in the time of Java 1.4 and has been falling in love with Java since then. Make friend with him on Facebook and watch his Java videos you YouTube.
Comments
That’s spring security thing. You need to sign in with username “user” and password from console