[JAVA] With Spring boot, password is hashed and member registration & Spring security is used to implement login function.

Overview

Introducing how to implement user authentication using Spring security. First, after introducing how to hash the password and register as a new member, We will show you how to authenticate users using Spring security.

This time, we have confirmed the operation in the following environment.

DB

Create a table to store usernames and passwords

First, create a table to store your username and password. This time, we will create a USER table with the user name (NAME), password (PASSWORD) and ID as columns. Execute the following SQL statement to create the USER table.

CREATE TABLE USER(ID INT PRIMARY KEY AUTO_INCREMENT, NAME VARCHAR(64) NOT NULL, PASSWORD VARCHAR(64) NOT NULL);

The important thing here is to set the PASSWORD to 60 or more characters. The password is hashed and registered in the DB, but if the password is hashed with BCryptPasswordEncoder used in this hashing, the number of characters will be 60 characters, so at least 60 characters are required for the password column this time.

Spring security preparation

Added Spring security dependency

First, inject Spring security dependencies to use Spring security. Add the following dependency to Pom.xml.

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>

Injecting a Spring security dependency will cause the Spring security default login screen to appear when you launch the application. At this stage, the following default login screen is displayed. スクリーンショット 2018-12-30 23.27.10.png

Now, let's display the login screen you created in the next section.

Make the login screen you created displayed

Here, create a login screen, create a Controller, and create WebSecurityConfig.java.

First, create a login screen.

Login.html


<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Login</title>
</head>
<body>
<form th:action="/" method="POST">

    <table align="center">
        <tr>
            <td>
User name:
            </td>
            <td>
                <input type="text" id="user" name="user"/>
            </td>
        </tr>
        <tr>
            <td>
password:
            </td>
            <td>
                <input type="password" id="password" name="password"/>
            </td>
        </tr>
        <tr>
            <td>
            </td>
            <td align="right">
                <button type="submit">Login</button>
            </td>
        </tr>
        <tr>
            <td>
            </td>
            <td align="right">
                <a href="/RegistrationForm">sign up</a>
            </td>
        </tr>
    </table>
</form>
</body>
</html>

Next, create a Controller.

LoginController.java


import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class LoginController {

	@RequestMapping("/login")
	public String showLoginForm(Model model) {

		//Transition to the login screen.
		return "Login";
	}
}

Next, configure with java config. Create a WebSecurityConfig class for configuration. This class inherits from WebSecurityConfigurerAdapter.

WebSecurityConfig.java


import org.springframework.context.annotation.Configuration;
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;

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

	@Override
	protected void configure(HttpSecurity http) throws Exception {
		//Specify the login page.
		//All access to the login page is allowed.
		http.formLogin()
	        .loginPage("/login")
		    .permitAll();

		http.authorizeRequests()
			.anyRequest().authenticated();
	}
}

In loginPage (), specify the path specified in RequestMapping of showLoginForm () method of LoginController class. By calling permitAll (), you can access the login page even if you are not authenticated. Call anyRequest (). authenticated () to authenticate all requests.

When you have completed this step, the login screen you created will be displayed as shown below. スクリーンショット 2019-01-06 18.42.06.png

Next, we will implement the membership registration function.

Implement member registration function

Hash the password and register it in the DB.

This chapter describes how to hash the password and register it in the DB. However, how to create a membership registration form and how to receive the input value of the form are the same as when the password is not hashed, so I will omit it. This section focuses on how to hash passwords. If you want to see all the source code, please see github for the source code.

This time, BCryptPasswordEncoder is used as the hashing method. Here, the PassswordEncoder class is included in two packages, org.springframework.security.crypto.password and org.springframework.security.authentiction.encoding, but the latter is deprecated, so the former I will use the one. First, add BCryptPasswordEncoder to the bean with java config.

WebSecurityConfig.java


import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

	@Override
	protected void configure(HttpSecurity http) throws Exception {
		//Specify the login page.
		//All access to the login page is allowed.
		http.formLogin()
			.loginPage("/login")
			.permitAll();

		http.authorizeRequests()
			.anyRequest().authenticated();
	}

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

This time, by setting the password and user name in the argument of the insertMemberInfo () method of the created mapper interface and calling it, insert the member information into the DB. Hash the password entered in the form before calling the insertMemberInfo () method.

RegisterMemberService.java


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.webapp.certificationtest.mapper.RegisterMemberMapper;

@Service
@Transactional
public class RegisterMemberService {

	@Autowired
	RegisterMemberMapper registerMemberMapper;

	@Autowired
	PasswordEncoder passwordEncoder;

	/**
	 *Register member information in the DB.
	 */
	public void registerMember(MemberRegistrationEntity entity) {

		//Hash the password and insertMemberInfo()Set to the object passed to.
		entity.setPassword(passwordEncoder.encode(entity.getPassword()));

		registMemberMapper.insertMemberInfo(entity);
	}
}

Here, we first declare PasswordEncoder with @ </ span> Autowired. Then, after calling encode () of PasswordEncoder to hash the password, insert the password into the DB.

Authenticate users using Spring security

Add settings with WebSecurityConfig

First, add to WebSecurityConfig.java.

WebSecurityConfig.java



import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

	@Override
	protected void configure(HttpSecurity http) throws Exception {
		//Specify the login page.
		//All access to the login page is allowed.
		http.formLogin()
			.loginPage("/login")
			.loginProcessingUrl("/authenticate")
			.usernameParameter("userName")
			.passwordParameter("password")
			.defaultSuccessUrl("/")
			.permitAll();

        //Added when the member registration function is implemented
		http.authorizeRequests()
			.antMatchers("/RegistrationForm").permitAll()
			.antMatchers("/Register").permitAll()
			.antMatchers("/Result").permitAll()
			.anyRequest().authenticated();
	}

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

Specify the URL to move to the authentication process with loginProcessingUrl (). Make sure that this URL matches the value of the action attribute of the form tag of the login form. In usernameParameter (), specify the value of the name attribute of the input tag that puts the user name in html. In passwordParameter (), specify the value of the name attribute of the input tag that puts the password in html. Specify the URL of the page to go to when login is successful with defaultSuccessUrl ().

Next, change the action and input names of the Login.html form.

Login.html


<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Login</title>
</head>
<body>
<form th:action="/authenticate" method="POST">

    <table align="center">
        <tr>
            <td>
User name:
            </td>
            <td>
                <input type="text" id="user" name="userName"/>
            </td>
        </tr>
        <tr>
            <td>
password:
            </td>
            <td>
                <input type="password" id="password" name="password"/>
            </td>
        </tr>
        <tr>
            <td>
            </td>
            <td align="right">
                <button type="submit">Login</button>
            </td>
        </tr>
        <tr>
            <td>
            </td>
            <td align="right">
                <a href="/RegistrationForm">sign up</a>
            </td>
        </tr>
    </table>
</form>
</body>
</html>

Next, create a class to put the username and password obtained from the USER table.

Account.java


/**
 *A class that stores member information required for login.
 */
public class Account {

	private String name;

	private String password;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getPassword() {
		return password;
	}

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

}

Next, write a mapper that gets the username and password from the USER table.

LoginMapper.java


import com.webapp.certificationtest.Account;

public interface LoginMapper {

	public Account findAccount(String name);
}

LoginMapper.xml


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.webapp.certificationtest.mapper.LoginMapper">

    <select id="findAccount" resultType="com.webapp.certificationtest.Account"
                             parameterType="String">
        SELECT
           NAME,
           PASSWORD
        FROM
           USER
        WHERE
           NAME = #{userName}

    </select>

</mapper>

Next, create a class to store user information. It inherits from the User class.

DbUserDetails.java


import java.util.Collection;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;

public class DbUserDetails extends User {
	//User information.
	private final Account account;

	public DbUserDetails(Account account,
			Collection<GrantedAuthority> authorities) {

		super(account.getName(), account.getPassword(),
				true, true, true, true, authorities);

		this.account = account;
	}

	public Account getAccount() {
		return account;
	}

}

The second argument of the constructor of the DbUserDetails class gives a list of permissions to grant to the user.

Next, create the Service class. This Service class gets the value from the User table and generates UserDetails. Authentication is performed by matching the information entered by the user in the login form with the User Details generated here.

DbUserDetailsService.java


import java.util.Collection;
import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.webapp.certificationtest.mapper.LoginMapper;

@Service
public class DbUserDetailsService implements UserDetailsService {

	@Autowired
	LoginMapper loginMapper;

	@Override
	@Transactional(readOnly = true)
	public UserDetails loadUserByUsername(String mailAddress)
			throws UsernameNotFoundException {
		//Get user information from DB.
		Account account = Optional.ofNullable(loginMapper.findOne(mailAddress))
				.orElseThrow(() -> new UsernameNotFoundException("User not found."));

		return new DbUserDetails(account, getAuthorities(account));
	}

	/**
	 *Set the range of authority given to this user when authentication is successful.
	 * @param account User information obtained from DB.
	 * @return A list of privilege ranges.
	 */
	private Collection<GrantedAuthority> getAuthorities(Account account) {
		//Set the range of authority given to the user when authentication is successful.
		return AuthorityUtils.createAuthorityList("ROLE_USER");
	}

}

If nothing can be retrieved from the User table, throw a UsernameNotFoundExceptionw. In the argument of UsernameNotFoundException, specify the message when the user name does not exist in the User table. In getAuthorities (), set the authority given to the user obtained from the User table. Change this method as needed. If you want to give multiple permissions, add them to the argument of createAuthorityList () separated by commas.

Next, add to WebSecurityConfig.java for authentication.

WebSecurityConfig.java


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.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.crypto.password.PasswordEncoder;

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

	@Autowired
	UserDetailsService userDetailsService;

	@Override
	protected void configure(HttpSecurity http) throws Exception {
		//Specify the login page.
		//All access to the login page is allowed.
		http.formLogin()
			.loginPage("/login")
			.loginProcessingUrl("/authenticate")
			.usernameParameter("userName")
			.passwordParameter("password")
			.defaultSuccessUrl("/")
			.permitAll();

		http.csrf().disable().authorizeRequests()
			.antMatchers("/RegistrationForm").permitAll()
			.antMatchers("/Register").permitAll()
			.antMatchers("/Result").permitAll()
			.anyRequest().authenticated();
	}

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

	@Autowired
	void configureAuthenticationManager(AuthenticationManagerBuilder auth) throws Exception {
		auth.userDetailsService(userDetailsService)
			.passwordEncoder(passwordEncoder());
	}
}

Finally, add it to LoginController.java.

LoginController.java


import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class LoginController {

	/**
	 *Transit to the login form.
	 */
	@RequestMapping("/login")
	public String showLoginForm(Model model) {

		//Transition to the login screen.
		return "Login";
	}

	/**
	 *Move to the main page.
	 *If the login is successful, this method will be called.
	 */
	@RequestMapping("/")
	public String login(Model model) {

		//Main page.
		return "index";
	}

}

Create a screen to be displayed after login.

index.html


<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Main page</title>
</head>
<body>
This is the main page.
</body>
</html>

Operation check

Check the operation.

Member registration

First is membership registration.

スクリーンショット 2019-01-06 18.42.06.png

Enter your user name and password to register. スクリーンショット 2019-01-06 18.45.41.png

スクリーンショット 2019-01-06 18.30.41.png

When the membership registration is completed, the password is hashed and inserted into the DB like this. スクリーンショット 2019-01-06 18.31.55.png

Login

Next is login.

Enter the user name and password you registered earlier. スクリーンショット 2019-01-06 18.46.52.png

I was able to log in and moved to the main page. スクリーンショット 2019-01-06 18.30.57.png

The source code up to this point is listed on github, so if you want to see all of them, please see that. If you have any questions, please contact me on twitter.

Recommended Posts

With Spring boot, password is hashed and member registration & Spring security is used to implement login function.
Try to implement login function with Spring Boot
[Introduction to Spring Boot] Authentication function with Spring Security
Login function with Spring Security
Implement login function simply with name and password in Rails (3)
Implement paging function with Spring Boot + Thymeleaf
Create login / logout function with Spring Security according to Spring official guide [For beginners]
Spring Security usage memo: Cooperation with Spring MVC and Boot
Spring Boot with Spring Security Filter settings and addictive points
Implement a simple Rest API with Spring Security with Spring Boot 2.0
Attempt to SSR Vue.js with Spring Boot and GraalJS
How to implement authentication process by specifying user name and password in Spring Boot
Implement GraphQL with Spring Boot
Implement REST API with Spring Boot and JPA (Application Layer)
Implement REST API with Spring Boot and JPA (Infrastructure layer)
Implement login function in Rails simply by name and password (1)
Until INSERT and SELECT to Postgres with Spring boot and thymeleaf
Connect to database with spring boot + spring jpa and CRUD operation
Implement REST API with Spring Boot and JPA (domain layer)
Implement a simple Rest API with Spring Security & JWT with Spring Boot 2.0
I implemented an OAuth client with Spring Boot / Security (LINE login)
Part 1: Try using OAuth 2.0 Login supported by Spring Security 5 with Spring Boot
Implemented authentication function with Spring Security ②
Implemented authentication function with Spring Security ③
Implemented authentication function with Spring Security ①
Implement login, user registration, and two-factor authentication with Docker x Laravel 8 Jetstream
I used Docker to solidify the template to be developed with spring boot.
"Teacher, I want to implement a login function in Spring" ① Hello World
HTTPS with Spring Boot and Let's Encrypt
Authentication function with Play Framework [Registration and authentication]
How to implement TextInputLayout with validation function
Implement CRUD with Spring Boot + Thymeleaf + MySQL
Login function implementation by Spring Security (securityConfig)
Add spring boot and gradle to eclipse
Achieve BASIC authentication with Spring Boot + Spring Security
Hash passwords with Spring Boot + Spring Security (with salt, with stretching)
How to use MyBatis2 (iBatis) with Spring Boot 1.4 (Spring 4)
How to use built-in h2db with spring boot
[Swift] How to implement the LINE login function
Try LDAP authentication with Spring Security (Spring Boot) + OpenLDAP
[Java / Spring Boot] Spring security ④ --Implementation of login process
Try to automate migration with Spring Boot Flyway
[Java] Article to add validation with Spring Boot 2.3.1.
I wanted to gradle spring boot with multi-project
Create Spring Cloud Config Server with security with Spring Boot 2.0