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
- Creating a new Spring Boot Project
- Creating the REST API
- Configuring Spring Security
- Creating a JPA Repository
- Implementing UserDetailService
- Implementing UserDetails
- Final Steps
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.

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.

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.

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

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.
@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.
Valuable information as always. Thank you for taking the time to share.