Spring Security Implementation using JPA

Spring Security Implementation using JPA

In this article I’ll be showing you guys how to implement authentication and authorization using Spring Security and JPA. Authentication refers to the process of confirming identity and Authorization refers to the process of verifying what a user has access to.

I’ve already explained in details regarding Authorization in my previous post on Spring Security Implementation using JDBC API. Check out the post to know more details about it.

In this article I’ll be explaining how we can use JPA instead of using JDBC to perform authentication. Unlike the JDBC implementation, Spring Security doesn’t have an out of the box implementation for JPA. Hence we must provide out own implementation for UserDetailsService and UserDetails. Check out the article on All you need to know about Spring Security to get a understanding about how Spring Security works, and what is UserDetailsService and UserDetails.

Overview

Create A Spring Boot Project

The first step to do is to create a new Spring Boot Project from start.spring.io. Use the following dependencies

  • Spring Web: To create the REST Endpoints
  • Spring Security: To implement authorization and authentication
  • Spring Data JPA: The Java Persistence APIs
  • H2 Database: The database to be used. Spring boot project will automatically create and initialize H2 DB if it is in the class path.
The steps mentioned in this article can be used with all databases includeing Oracle, Postgres, Maria etc You just have change the DataSource being used.
Spring Boot Project
Spring Boot Project Configurations

Our final package structure will look as shown below once all the classes and optional scripts have been created. You can use the below image as a reference while following the tutorial.

Final Package Structure

Creating the REST API

To create the REST API, create a class name Controller.java under the package controller.

  • The make this a controller add the annotation @RESTController to the class.
  • Create three methods name guest, user and admin returns a String.
  • Add @GetMapping to each of those method and provide the endpoints
package in.notyouraveragedev.jpasecurity.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class Controller {

	@GetMapping("/")
	public String test() {
		return "<h1>Welcome</h1>";
	}
}

If you run the application you’ll be treated with a login page provided by Spring Security. To know more details on how this page is being displayed, check out the article on All you need to know about spring security.

login
Default Login Page

The default username is user and the password is an auto generated that can be obtained from the console.

default password
Default Generated Password

If no configuration is provided Spring Security will use this default credentials and login page. Now lets provide some security configurations implement authentication and authorization the way we need it.

Configuring Spring Security

To provide any kind of configurations you’ll need to create a configuration class that extends WebSecurityConfigureAdapter. The WebSecurityConfigurerAdapter will provide the instances of AuthenticationManagerBuilder for configuring authentication and an instance of HttpSecurity to configure the granted authority or the authorization part.

  • So, create a class name SecurityConfiguration which extends the WebSecurityConfigurerAdapter.
  • Override the methods configure(AuthenticationManagerBuilder) and configure(HttpSecurity)
  • @EnableWebSecurity annotation must be specified for the class for spring to use this new class for security configurations
package in.notyouraveragedev.jpasecurity.security;

import org.springframework.beans.factory.annotation.Autowired;
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 in.notyouraveragedev.jpasecurity.service.MyUserDetailsSerivce;

@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        super.configure(auth);
	}

	@Override
	protected void configure(HttpSecurity http) throws Exception {
        super.configure(http);
	}

}

As I have mentioned before, Spring Security doesn’t have an out of the box implementation for JPA. So we must provide our own implementation for the UserDetailsService. So first, we must inform Spring Security that we will be using our own UserDetailsService implementation using the AuthenticationManagerBuilder as shown below. You can configure your authorization part now itself.

Because we have an H2 dependency in my class path, Spring will automatically create a DataSource that we can use.
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

	@Autowired
	private MyUserDetailsSerivce myUserDetailsSerivce;
	
	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		// as for the authentication part, unlike JDBC spring doesn't have an out of the box
		// implementation for JPA that you can use.
		// for jpa we need to have out own implementation for UserDetailsService and return a UserDetails
		// Read through blog post on spring security for more details
		
		auth.userDetailsService(myUserDetailsSerivce);
	}

	@Override
	protected void configure(HttpSecurity http) throws Exception {
		// the authorities / authorization part
		http.authorizeRequests().antMatchers("/").hasRole("USER").and().formLogin();
	}

}

Creating a JPA Repository

Before we can create our implementation for UserDetailsService, lets create a new JpaRepository interface and add necessary methods.

  • Create a new interface named MyRepository that extends JpaRepository and annotate it using the stereotype @Repository.
  • Add a method signature to fetch data by username. The implementation for this method will be created by JPA.
package in.notyouraveragedev.jpasecurity.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import in.notyouraveragedev.jpasecurity.models.User;

@Repository
public interface MyRepository extends JpaRepository<User, Long>{

	// implementation for this method will be created automatically by jpa
	public User findByUsername(String username);
}

The entity class I’m using contains a username, password and a comma separated String of roles

package in.notyouraveragedev.jpasecurity.models;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class User {

	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	private Long id;
	@Column
	private String username;
	@Column
	private String password;
	@Column
	private String role;

	public User() {
		super();
	}

	public User(String username, String password, String role) {
		super();
		this.username = username;
		this.password = password;
		this.role = role;
	}

	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	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;
	}

	public String getRole() {
		return role;
	}

	public void setRole(String role) {
		this.role = role;
	}

	@Override
	public String toString() {
		return "User [id=" + id + ", username=" + username + ", password=" + password + ", role=" + role + "]";
	}

}

UserDetailsService Implementation

Create a new class named MyUserDetailsSerivce that implements UserDetailsSerivce and override the method loadUserByUsername(). Annotate this class using the stereotype @Service. You can Autowire the repository that we have created in the previous step here.

  • The method loadUserByUsername() accepts a username and returns a UserDetails.
  • Implementation for this UserDetails also needs to be provided by us.
  • In the loadUserByUsername method, we can use our JPA repository to fetch data from database and return an instance of UserDetails using this fetched data.
package in.notyouraveragedev.jpasecurity.service;

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.notyouraveragedev.jpasecurity.models.MyUserDetails;
import in.notyouraveragedev.jpasecurity.models.User;
import in.notyouraveragedev.jpasecurity.repository.MyRepository;

@Service
public class MyUserDetailsSerivce implements UserDetailsService{

	@Autowired
	private MyRepository myRepository;
	
	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {		
		User user = myRepository.findByUsername(username);
		return new MyUserDetails(user);
	}

}

UserDetails Implementation

In addition to implementing the UserDetailsService, we also need to create an implementaion for UserDetails. This UserDetails object will be used by Spring Security to perform authentication. UserDetails is nothing but a POJO class with some Spring specific attributes.

  • The MyUserDetails class I’m hard coding all of the flags.
  • Only username, password and roles (comma separated string in database) will be dynamic.
package in.notyouraveragedev.jpasecurity.models;

import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

public class MyUserDetails implements UserDetails {

	private String username;
	private String password;
	private List<String> roles; // comma separated

	public MyUserDetails(String username) {
		this.username = username;
	}

	public MyUserDetails(User user) {
		this.username = user.getUsername();
		this.password = user.getPassword();
		this.roles = Arrays.asList(user.getRole().split(","));
	}

	@Override
	public Collection<? extends GrantedAuthority> getAuthorities() {
		return roles.stream().map(role -> new SimpleGrantedAuthority(role)).collect(Collectors.toList());
	}

	@Override
	public String getPassword() {
		return this.password;
	}

	@Override
	public String getUsername() {
		return this.username;
	}

	@Override
	public boolean isAccountNonExpired() {
		return true;
	}

	@Override
	public boolean isAccountNonLocked() {
		return true;
	}

	@Override
	public boolean isCredentialsNonExpired() {
		return true;
	}

	@Override
	public boolean isEnabled() {
		return true;
	}

}

Final Steps

With this we have successfully configured Spring Security to work in JPA. Before we can test we need to do a few more steps in the Application Main class.

  • Specify the packages containing JPA repository using the @EnableJpaRepositories
  • Add a PasswordEncoder Bean
  • Insert some data in the database to test. Here I’m using the ApplicationReadyEvent and an @EventListener to insert the data.
package in.notyouraveragedev.jpasecurity;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.annotation.Bean;
import org.springframework.context.event.EventListener;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

import in.notyouraveragedev.jpasecurity.models.User;
import in.notyouraveragedev.jpasecurity.repository.MyRepository;

@SpringBootApplication
@EnableJpaRepositories(basePackages = { "in.notyouraveragedev.jpasecurity.repository" })
public class JpaSecurityApplication {

	@Autowired
	private MyRepository myRepository;

	public static void main(String[] args) {
		SpringApplication.run(JpaSecurityApplication.class, args);
	}

	@Bean
	PasswordEncoder getPasswordEncoder() {
		return NoOpPasswordEncoder.getInstance();
	}

	@EventListener
	public void applicationReady(ApplicationReadyEvent event) {
		myRepository.save(new User("username", "password", "ROLE_USER"));
		myRepository.save(new User("user", "pass", "ROLE_USER"));
	}
}

Now you can run the application and test and make sure that every thing is working as expected. If you face any issues or have any doubts please feel free to ask them in the comment section below and I’ll be happy to assist.

One thought on “Spring Security Implementation using JPA

Leave a Reply

Your email address will not be published. Required fields are marked *

Back To Top