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.