Home > Back-end >  How to implement an authentication method using Spring Boot and JPA?
How to implement an authentication method using Spring Boot and JPA?

Time:01-22

Using Spring Boot and JPA in Java, I am creating a vending machine simulator whereby there are 2 roles: "BUYER" and "SELLER". I have a table called users in a MySQL database that stores users along with their usernames, passwords and their roles. I would like to implement an authentication method that allows me to specify which endpoints can be called based on the role of the user (e.g. anyone can create a new user, buyers can purchase products from the vending machine and sellers can add new products). Most of the tutorials I have seen have been using an old version of Spring Boot Security and using things like the WebSecurityConfigurerAdapter which have since been deprecated and therefore I cannot follow the tutorials and other ones are using h2 or other in memory databases. How can I implement this authentication using MySQL.

Worth noting I am using Postman to test my requests.

users table:

 ---------- --------------- ------ ----- --------- ---------------- 
| Field    | Type          | Null | Key | Default | Extra          |
 ---------- --------------- ------ ----- --------- ---------------- 
| id       | int           | NO   | PRI | NULL    | auto_increment |
| deposit  | decimal(38,2) | NO   |     | NULL    |                |
| password | varchar(255)  | NO   |     | NULL    |                |
| role     | varchar(255)  | NO   |     | NULL    |                |
| username | varchar(255)  | NO   | UNI | NULL    |                |
 ---------- --------------- ------ ----- --------- ---------------- 

Additional bonus, how can I encrypt the user's password when saving to the database?

CodePudding user response:

instead of WebSecurityConfigurerAdapter I am using the following configuration to add BASIC authentication and require Authority ADMIN for /api/admin* endpoint, than permit some html, css, js files. Could be a start...

@Configuration
    protected static class SecurityConfiguration
    {
        @Bean
        public SecurityFilterChain filterChain(HttpSecurity http) throws Exception
        {   
            http.httpBasic()
                .and().authorizeHttpRequests()
                .requestMatchers("/api/admin/*")
                .hasAuthority("ADMIN")
                .requestMatchers("/createuser","/index.html", "/", "/home", "/login", "/*.css", "/*.js", "/favicon.ico")
                .permitAll()
                .anyRequest().authenticated()
                .and().csrf()
                .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
            return http.build();
        }

    }

CodePudding user response:

To configure the authorization in new manner implement a configuration class like this:

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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
public class SecurityConfigurer {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .authorizeHttpRequests(requests -> requests
                        .requestMatchers("/", "/signup", "register").permitAll() // Permit all users create users
                        .requestMatchers("/buyers/**").hasAuthority("ROLE_BUYER") // Restrict buyers endpoints to only the buyer role
                        .requestMatchers("/sellers/**").hasAuthority("ROLE_SELLER") // Restrict sellers endpoints to only the seller role
                );
        return http.build();
    }

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

In the snippet above password manager is configured as a bean. In your save method, You can inject and use it to hash the password. Also for authentication and loading your database users, Implement a class like the following code:

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.Component;

@Component
public class SecurityUserLoader implements UserDetailsService {

    private final UserRepository userRepository;

    public SecurityUserLoader(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User loadedUser = userRepository.findByEmail(username)
                .orElseThrow(() -> new UsernameNotFoundException("Could not found a user with given name"));
        return new org.springframework.security.core.userdetails.User(
                loadedUser.email,
                loadedUser.password,
                true,
                true,
                true,
                true,
                Collections.singleton(new SimpleGrantedAuthority(loadedUser.getRole()))
        );
    }
}

To explain these codes briefly, The first snippet codes and configs tell Spring what roles can access what endpoints. The second snippet is used in authentication when a user password is retrieved from the database to match it with credentials. Then finally if everything was fine it's role will be determined.

  • Related