Hey guys in this post, we will discuss implementing a password encoder in the Spring Security application using BCryptPasswordEncoder. It is one of the implementations of PasswordEncoder. This is the continuation of the previous post, please follow that post before proceeding with this.
Table of Contents
Complete example
Let’s create a step-by-step spring boot project and create our own implementation of UserDetailsService
Create database and tables
In order to create our own custom implementation of UserDetailsService
, first we need to create database tables for our users. To create a database and tables execute the following query
CREATE database springsecurity;
USE springsecurity;
CREATE TABLE tbl_employees
(
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
email VARCHAR(255) NOT NULL,
pwd VARCHAR(255) NOT NULL,
role VARCHAR(255) NOT NULL
);
INSERT INTO tbl_employees VALUES (NULL, "[email protected]", "$2y$12$ZYU8aMJQBkxCF0DK9zLfnuTRP3RLVEI6PS/wbdESPth9MwCmSDRv.", "admin");
So in this example, we are not discussing creating a new user, so in order to hash the password, you can visit the website https://bcrypt-generator.com/ to hash the password.
Create spring boot project
There are many different ways to create a spring boot application, you can follow the below articles to create one –
>> Create spring boot application using Spring initializer
>> Create spring boot application in Spring tool suite [STS]
>> Create spring boot application in IntelliJ IDEA
Add maven dependencies
Open pom.xml
and add the following dependencies –
<?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.5.0</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>in.bushansirgur</groupId>
<artifactId>springsecurityproject</artifactId>
<version>v1</version>
<name>springsecurityproject</name>
<description>Spring security project</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<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>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</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>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
spring-boot-starter-web
dependency for building web applications using Spring MVC. It uses the tomcat as the default embedded container. spring-boot-starter-security
dependency, which will help to implement spring security. mysql-connector-java
dependency for connecting to the MySQL database. spring-boot-starter-data-jpa
dependency to interact with the database and perform the database operations.
Configure datasource
Open application.properties
file and add the following contents –
spring.datasource.url=jdbc:mysql://localhost:3306/springsecurity
spring.datasource.username=scbushan05
spring.datasource.password=scbushan05
Create a Rest controller
Create HomeController.java
inside the in.bushansirgur.springboot.controller
package and add the following content
package in.bushansirgur.springboot.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HomeController {
@RequestMapping("/home")
public String showHomePage () {
return "displaying the home page contents";
}
@RequestMapping("/protected")
public String protectedPage () {
return "displying the protected page contents";
}
}
We have created two handler methods showHomePage()
, which is mapped to /home
, anyone can access this URI and protectedPage()
, which is mapped to /protected
, only authorized users can access this URI.
Create an entity class
Create an entity class Employee.java
inside the in.bushansirgur.springboot.entity
package and add the following content
package in.bushansirgur.springboot.entity;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity(name = "tbl_employees")
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String email;
private String pwd;
private String role;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
}
This is just a POJO with private fields, setters, getters, and JPA annotations. The class is annotated with @Entity
annotation, this represents that this class is mapped with a database table.
Create a Repository
Create an interface EmployeeRepository.java
inside the in.bushansirgur.springboot.repository
package and add the following content
package in.bushansirgur.springboot.repository;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import in.bushansirgur.springboot.entity.Employee;
@Repository
public interface EmployeeRepository extends JpaRepository<Employee, Integer> {
List<Employee> findByEmail(String email);
}
Here we are creating a query method findByEmail()
to retrieve the list of employees.
Create a service
Next, we need to create a service class that implements the UserDetails
. Create a class EmployeeService.java
inside the in.bushansirgur.springboot.service
package and add the following content
package in.bushansirgur.springboot.service;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import in.bushansirgur.springboot.entity.Employee;
public class EmployeeService implements UserDetails {
/**
*
*/
private static final long serialVersionUID = 1L;
private final Employee employee;
public EmployeeService (Employee employee) {
this.employee = employee;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority(employee.getRole()));
return authorities;
}
@Override
public String getPassword() {
return employee.getPwd();
}
@Override
public String getUsername() {
return employee.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;
}
}
Here we create a constructor and pass the Employee
object. So in the future whoever, creating EmployeeService
they need to pass the Employee
object as well. With that Employee
object, we will retrieve the username
, password
and authorities
.
Next, we need to create one more service class that implements UserDetailsService
because we need to tell Spring security that AppUserDetails
is the implementation for UserDetailsService
. Create a class AppUserDetails.java
inside the in.bushansirgur.springboot.config
package and the following content
package in.bushansirgur.springboot.config;
import java.util.List;
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;
import org.springframework.stereotype.Service;
import in.bushansirgur.springboot.entity.Employee;
import in.bushansirgur.springboot.repository.EmployeeRepository;
import in.bushansirgur.springboot.service.EmployeeService;
@Service
public class AppUserDetails implements UserDetailsService {
@Autowired
private EmployeeRepository eRepository;
@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
List<Employee> employees = eRepository.findByEmail(email);
if (employees.size() == 0) {
throw new UsernameNotFoundException("User details not found for the user "+email);
}
return new EmployeeService(employees.get(0));
}
}
Here we will override the loadUserByUsername(String email)
to write the business logic. Inside this method, we fetch the user from the database by calling the repository method findByEmail()
. If the user found then we create the EmployeeService
object by passing the user to the constructor of the EmployeeService
.
Create a configuration class
Let’s customize the spring security to deny all the URIs. Create ProjectSecurityConfig.java
inside the in.bushansirgur.springboot.config
package and add the following content.
package in.bushansirgur.springboot.config;
import javax.sql.DataSource;
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.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.JdbcUserDetailsManager;
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/home").permitAll()
.antMatchers("/protected").authenticated()
.and()
.formLogin()
.and()
.httpBasic();
}
@Bean
public PasswordEncoder passwordEncoder () {
return new BCryptPasswordEncoder();
}
}
Run the app
Run the application using the below maven command –
mvn spring-boot:run
Open the browser and enter the following URL –
http://localhost:8080/home
http://localhost:8080/protected
Enter the username and password which we inserted inside the database. Spring security will authenticate and allow the user to see the contents.
That’s it for this post, if you like this post, share this with your friends and colleagues or you can share this within your social media platform. Thanks, I will see you in our next post.