In this tutorial, you will learn how to add Spring Security to your Spring Boot REST API and require authentication to be able to access some routes in the API. Then you well implement User Registration and create users.
Finally, we would configure our React application to authenticate against the Spring Boot API.
- Configure Spring Security at the Backend
- Implement UserDetailsService
- Implement UserDetails, Update the Controller and Service
- Configure CORS
- Create a New User and Test the Login
- Setup AxiosConfig to use .env Parameters
1. Configure Spring Security at the Backend
If you have not setup Spring Security in your Spring Boot application, please follow the steps below. You can also watch the step by step video tutorial here.
However the basic steps are as follows:
Step 1 – Create the SecurityConfig file and in it, do the following:
- Create the User model, repository, service and controller
- Annotate the class with @Configuration and @WebSecurity annotations
- Configure the SecurityFilterChain bean
- Create the UserDetails bean
- Create the BCryptPasswordEncoder bean
- Create an configure the Authentication provider, setting the UserDetails and password encoder
The SecurityFilterChain bean is given below:
@Bean public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception { return httpSecurity .csrf(AbstractHttpConfigurer::disable) .authorizeHttpRequests(auth -> auth .requestMatchers("/register").permitAll() .requestMatchers("/login").permitAll() .anyRequest().authenticated() ) .httpBasic(Customizer.withDefaults()) .build(); }
The Authentication Provider bean is given below:
@Bean public AuthenticationProvider authenticationProvider() { DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); provider.setUserDetailsService(userDetailsService); provider.setPasswordEncoder(bCryptPasswordEncoder()); return provider; }
Step 2 – Implement the UserDetailsService
Create a class called MyUserDetailsService that implements UserDetailsService.
Override the loadUserByUsername and update the UserRepository
You will also need to throw an exception is the user is null.
Finally return a UserPrincipal(create in Step 3)
Step 3 – Implement UserDetails, Update the Controller and Service
Create a class UserPrincipal that implements UserDetails.
Add a field User in and add a constructor
Also ensure that the getAuthorities() method returns as shown below:
return Collections.singleton(new SimpleGrantedAuthority("USER"));
Step 4 – Configure CORS
We would have to configure Cross Origin Resource Sharing (CORS) to allow access from requests coming from our React application.
Create a CorsConfigurationSource bean as shown below.
@Bean CorsConfigurationSource corsConfigurationSource() { CorsConfiguration configuration = new CorsConfiguration(); configuration.setAllowedOrigins(List.of("http://localhost:3001")); configuration.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE")); configuration.setAllowCredentials(true); configuration.addAllowedHeader("*"); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", configuration); return source; }
Next, add this to the SecurityFilterChain
.cors(Customizer.withDefaults())
Step 5 – Create New User and Test the Login
Using Postman client, make a POST request to the /register, specifying the user details in the request body. No Authentication is used.
Then make a GET request to the /products endpoint and using Basic Authentication, enter the username and password
Now, we need to specify the username and password in axios get request. This is specified as the second parameter to the get() method.
Step 6 – Setup AxiosConfig in React Application
This means we would place the user credentials in a .env file and then configure our application to read the environment variables. Follow the steps below:
1. Create a .env file and place the following inside it. Replace the yourusername and yourpassword accordingly
REACT_APP_API_USERNAME=yourusername REACT_APP_API_PASSWORD=yourpassword
2. Create a file called axiosConfig.js and this would be the content
import axios from 'axios'; const axiosInstance = axios.create({ // You can also add default headers here headers: { 'Content-Type': 'application/json' } }); axiosInstance.interceptors.request.use( (config) => { const username = REACT_APP_API_USERNAME; const password = REACT_APP_API_PASSWORD; if (username && password) { config.auth = { username: username, password: password }; } return config; }, (error) => { return Promise.reject(error); } ); export default axiosInstance;
3. Since we have setup the config, you can now replace all the use of axios with axiosInstance.