Home » Framework » Spring Security Authentication with MySql using JPA

Spring Security Authentication with MySql using JPA

In this article, we are going to see how to do authentication with the MySql database using JPA in the Spring Boot application.

JPA is a Java Persistence API that is used to persist the data between the java objects and database tables. Here let me use this to fetch the user information from the MySql database and do the authentication with Spring Security.

We saw spring security authentication with the H2 database with some hard-coded users. But here we use a real database MySql to do the spring security authentication.

Spring-Security-JPA-MySql

Here let me explain the authentication process in two ways. One is generating the user details object with some hard-coded values without JPA and MySql. The second one is generating the user details object with the user information from the MySql database using JPA.

Spring Security Authentication through UserDetailsService with hardcoded users

First, we have to create the Spring Boot Project in spring.initializr with needed dependencies. Then we have to configure the authentication and authorization part.

The needed dependencies are Spring Web, Spring Security, Spring Boot DevTools, Spring Boot Actuator, Spring Boot JPA, and MySql Driver.

Once we created the project with the above dependencies then we have to create the controller class with some endpoints and we have to create the security configuration class to do the authentication and authorization.

The Controller class is below

package com.tipstocode.jpamysqlsecurity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HomeController {
	
	@GetMapping("/")
	public String login(){
		return "Welcome to TipsToCode Site!";
	}	
	@GetMapping("/user")
	public String user(){
		return "Welcome user to TipsToCode Site!";
	}	
	@GetMapping("/admin")
	public String admin(){
		return "Welcome admin to TipsToCode Site!";
	}
}

The below class is the security configuration of the Spring Boot application.

package com.tipstocode.jpamysqlsecurity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
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.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter{
	
	@Autowired
	UserDetailsService userDetailsService;
	
	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception{		
		auth.userDetailsService(userDetailsService);		
	}
	
	@Bean
	public PasswordEncoder getPasswordEncoder(){
		return NoOpPasswordEncoder.getInstance();
	}
	
	@Override
	protected void configure(HttpSecurity http) throws Exception{
		
		http.authorizeRequests()
			.antMatchers("/admin").hasRole("ADMIN")
			.antMatchers("/user").hasAnyRole("USER", "ADMIN")
			.antMatchers("/").permitAll()
			.and().formLogin();
	}
}

The above configuration does the authorization like the URL "/" is accessible by all the users irrespective of roles and the URL "/user" is accessible by the users of a role "ROLE_USER" and "ROLE_ADMIN". The URL "/admin" is only accessible by the user of role "ROLE_ADMIN".

NoOpPasswordEncoder is used to authenticate the users with a clear-text password. But in real-world applications, we should use encrypted passwords only. For explaining easily here I used this NoOpPasswordEncoder.

Now let me explain how the authentication happens through this UserDetailsService. The below step-by-step process explains the authentication flow.

Step 1

Once the application starts the method "loadUserByUsername" from UserDetailsService is called automatically. This method takes the username as an argument and returns the UserDeatils object.

Step 2

The logic is written inside the method fetch the user's information from the database and binds that with the UserDeatils object and return the same.

Step 3

Spring Security looks into the UserDeatils object to do the authentication.

The below diagram shows the flow of the authentication process in Spring Security.

Spring-Security-UserDetails-Service
Spring-Security-UserDetails-Service

Now, let's start to create the UserDetailsService and UserDetails objects. Create the class called "MyUserDetailsService" which implements the UserDetailsService like below.

package com.tipstocode.jpamysqlsecurity;
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;

@Service
public class MyUserDetailsService implements UserDetailsService{
	
@Override
	public UserDetails loadUserByUsername(String userName)throws UsernameNotFoundException {
		
		return new MyUserDetails(userName);
		
	}
}

Now let's create the class called "MyUserDetails" which implements the UserDetails like below.

package com.tipstocode.jpamysqlsecurity;
import java.util.Arrays;
import java.util.Collection;
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;

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

	@Override
	public Collection<? extends GrantedAuthority> getAuthorities() {
		// TODO Auto-generated method stub
		return Arrays.asList(new SimpleGrantedAuthority("ROLE_USER"));
	}
	@Override
	public String getPassword() {
		// TODO Auto-generated method stub
		return "password";
	}
	@Override
	public String getUsername() {
		// TODO Auto-generated method stub
		return userName;
	}
	@Override
	public boolean isAccountNonExpired() {
		// TODO Auto-generated method stub
		return true;
	}
	@Override
	public boolean isAccountNonLocked() {
		// TODO Auto-generated method stub
		return true;
	}
	@Override
	public boolean isCredentialsNonExpired() {
		// TODO Auto-generated method stub
		return true;
	}

	@Override
	public boolean isEnabled() {
		// TODO Auto-generated method stub
		return true;
	}	
}

The fields of "MyUserDetails" class are configured with some hard coded values like password as "password" and active as "true" and so on. The constructor generates the user details with these hard coded values based on the username which is coming from the login.

Once the application starts the method "loadUserByUsername" generates the UserDetails object based on the given username and passes it to spring security to do authentication.

Everything is ready. Now, let's start to test it. Open this URL "http://localhost:8080/user" in the browser. It shows the login form then you can put any username and password as "password" to login.

Once authenticated successfully then you can see this message in the browser like "Welcome user to TipsToCode Site!".

Actually, the above configuration generates the UserDetails object with the hard-coded values for any username given at the time of login. Then Spring Security looks into this UserDetails object to do the authentication.

Here there is no part of JPA and MySql at all. Now let see how to authenticate the users from the MySql database using JPA.

Spring Security Authentication with MySql Database using JPA

What we are going to do here is, we have to fetch the user information from the MySql database using JPA and binds the user information with the UserDetails object to do the authentication. Let's start to do it.

First, we have to set up the MySql database with the tables needed. So let's create the database named "springsecurity" and create the table named "user".

The structure and the data of the user table are like below.

idpasswordactiveuser_nameroles
1password1tipstocode_userROLE_USER
2password1tipstocode_adminROLE_ADMIN

Now the database setup is ready and there are two users available in the user table. Let's start to do the changes on the application end.

We have to create the model class named "User" which has the same fields as the user table in the MySql database.

package com.tipstocode.jpamysqlsecurity;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "User")
public class User {
	
	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	private int id;	
	private String userName;	
	private String password;	
	private boolean active;	
	private String roles;

	public int getId() {
		return id;
	}
	public void setId(int 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 boolean isActive() {
		return active;
	}
	public void setActive(boolean active) {
		this.active = active;
	}

	public String getRoles() {
		return roles;
	}
	public void setRoles(String roles) {
		this.roles = roles;
	}
}

Here JPA persists the data of the user table into the User object. Now let's create the JPA repository to fetch the user information from the MySql database.

Create the interface UserRepository that extends the JpaRepository like below.

package com.tipstocode.jpamysqlsecurity;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;

public interface UserRepository extends JpaRepository<User, Integer>{	
	
	Optional<User> findByUserName(String userName);

}

In the above repository, the method "findByUserName" fetches the user information from the MySql database based on the given user name. We have to call this method inside the "loadUserByUsername" method to fetch the user information.

So the changes of "MyUserDetailsService" class are like below.

package com.tipstocode.jpamysqlsecurity;
import java.util.Optional;
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;

@Service
public class MyUserDetailsService implements UserDetailsService{
	
	@Autowired
	UserRepository userRepository;

	@Override
	public UserDetails loadUserByUsername(String userName)throws UsernameNotFoundException {
		
		Optional<User> user = userRepository.findByUserName(userName);		
		user.orElseThrow(()-> new UsernameNotFoundException("Not Found: "+ userName));		
		return user.map(MyUserDetails::new).get();			
		
	}
}

In the above snippet, the method "loadUserByUsername" calls the repository to fetch the user information. This user information automatically binds with the UserDetails object then spring security does the authentication with that object.

The final one is we have to change the "MyUserDetails" class like below in order to accept the user information from the database.

package com.tipstocode.jpamysqlsecurity;
import java.util.Arrays;
import java.util.Collection;
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 boolean active;	
	private List<GrantedAuthority> authorities;	
	
	public MyUserDetails(User user){
		this.userName = user.getUserName();
		this.password = user.getPassword();
		this.active = user.isActive();
		this.authorities = Arrays.stream(user.getRoles().split(","))
				.map(SimpleGrantedAuthority::new)
				.collect(Collectors.toList());
	
	}
	@Override
	public Collection<? extends GrantedAuthority> getAuthorities() {
		// TODO Auto-generated method stub
		return authorities;
	}
	@Override
	public String getPassword() {
		// TODO Auto-generated method stub
		return password;
	}
	@Override
	public String getUsername() {
		// TODO Auto-generated method stub
		return userName;
	}
	@Override
	public boolean isAccountNonExpired() {
		// TODO Auto-generated method stub
		return true;
	}
	@Override
	public boolean isAccountNonLocked() {
		// TODO Auto-generated method stub
		return true;
	}
	@Override
	public boolean isCredentialsNonExpired() {
		// TODO Auto-generated method stub
		return true;
	}
	@Override
	public boolean isEnabled() {
		// TODO Auto-generated method stub
		return active;
	}
}

Now, how the JPA knows to fetch the data from the MySql database? Right!. For this, we have to configure the MySql connection properties in the application.properties file like below.

spring.datasource.url=jdbc:mysql://localhost:3306/springsecurity
spring.datasource.username=root
spring.datasource.password=
spring.jpa.hibernate.ddl-auto=update
spring.jpa.hibernate.naming.strategy=org.hibernate.cfg.ImprovedNamingStrategy

Now we have to enable this UserRepository in the application like below.

package com.tipstocode.jpamysqlsecurity;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

@SpringBootApplication
@EnableJpaRepositories(basePackageClasses = UserRepository.class)
public class JpaMysqlSecurityApplication {

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

}

Now everything is ready. Let start to test the application. Keep the MySql database is running and start the application. Once started successfully please type this URL in browser "http://localhost:8080/user" then it shows login screen to authenticate.

You have to put the username as "tipstocode_user" and password as "password" which is the same as in the database to log in. Once the authentication successful then you see a message like "Welcome user to TipsToCode Site!".

Like the same way, you can test with admin user through this URL "http://localhost:8080/admin".

Hope you understood how to do spring security authentication with the MySql database using JPA. Thanks!. Keep Reading!.

Leave a Reply

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