I am trying to write a program, that has three accessibility modes for its APIs : Public, User and Admin. So I am trying to configure the spring security to assess whether someone can access based on the endpoint and also the role they have. The endpoints having /public** will not require any credentials or role-based checks, while the User and Admin will need the correspoinding credentials and role. Now I am testing the APIs with postman. for public ones I had no issue. but for User and Admin endpoints I am getting blocked even before hitting the controller and UserService.
I am using logs in different parts of the program to understand what is stopping it. I realised it gets blocked right on the spot, and does not accept the credentials and I receive a 403 error (I am using the users and admins already registered in DB, and I am sure the credentials are correct). Here is the logs I get:
2023-11-03T15:40:03.904+01:00 INFO 8812 --- [nio-8080-exec-2] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2023-11-03T15:40:03.904+01:00 INFO 8812 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2023-11-03T15:40:03.906+01:00 INFO 8812 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : Completed initialization in 2 ms
2023-11-03T15:40:03.978+01:00 DEBUG 8812 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy : Securing GET /admin/users/
2023-11-03T15:40:04.022+01:00 DEBUG 8812 --- [nio-8080-exec-2] o.s.s.w.a.AnonymousAuthenticationFilter : Set SecurityContextHolder to anonymous SecurityContext
2023-11-03T15:40:04.084+01:00 DEBUG 8812 --- [nio-8080-exec-2] o.s.s.w.s.HttpSessionRequestCache : Saved request http://localhost:8080/admin/users/?continue to session
2023-11-03T15:40:04.085+01:00 DEBUG 8812 --- [nio-8080-exec-2] o.s.s.w.a.Http403ForbiddenEntryPoint : Pre-authenticated entry point called. Rejecting access
2023-11-03T15:40:04.094+01:00 DEBUG 8812 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy : Securing GET /error
2023-11-03T15:40:04.100+01:00 DEBUG 8812 --- [nio-8080-exec-2] o.s.s.w.a.AnonymousAuthenticationFilter : Set SecurityContextHolder to anonymous SecurityContext
2023-11-03T15:40:04.103+01:00 DEBUG 8812 --- [nio-8080-exec-2] o.s.s.w.s.HttpSessionRequestCache : Saved request http://localhost:8080/error?continue to session
2023-11-03T15:40:04.103+01:00 DEBUG 8812 --- [nio-8080-exec-2] o.s.s.w.a.Http403ForbiddenEntryPoint : Pre-authenticated entry point called. Rejecting access
since it is invoking AnonymousAuthenticationFilter it means that it is not accepting my credentials from db. I am certain the connection to db is correct because I have tried all the endpoints before adding security.
Here is the LoadByUserName() method in my UserService which is supposed to help with database-backed authentication:
@Override //for Spring Security and authentication measures using username and password credentials.
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
logger.info("trying to load user info using username {}", username);
User user= repository.findByUserName(username);
if (user == null) {
throw new UsernameNotFoundException("User not found with username: " + username);
}
logger.info("Loaded user: {} with role: {}", user.getUsername(), user.getRole());
// Create and return a UserDetails object based on the retrieved User entity
return new org.springframework.security.core.userdetails.User(
user.getUsername(),
user.getPassword(),
AuthorityUtils.createAuthorityList("ROLE_" + user.getRole().name())
);
}
Here is my securityConfiguration:
package com.easytravel.userManagementService.config;
import com.easytravel.userManagementService.model.Role;
import com.easytravel.userManagementService.userService.UserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
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.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity
public class SecurityConfig{
@Autowired
@Lazy //lazy initialization to avoid circular dependency with springSecurity
private UserService userService ;
public static final Logger logger= LoggerFactory.getLogger(SecurityConfig.class);
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth, UserService userService) throws Exception{ //using configureGlobal which will use userService to configure authentication
logger.info("configureGlobal method invoked");
auth.userDetailsService(userService);
}
//Configuring access control
@Bean
public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
logger.info("securityFilterChain method invoked. checking for correct roles based on path.");
http.csrf(x-> x.disable()); //Cross-site Request Forgery
http
.authorizeHttpRequests(authorize ->
authorize
.requestMatchers(HttpMethod.POST, "/public/**").permitAll()
// .requestMatchers(HttpMethod.GET, "/public/**").permitAll()
.requestMatchers("/user/users/**").hasAnyRole(Role.AUTH_USER.name(), Role.ADMIN.name())
.requestMatchers("/admin/users/**").hasRole(Role.ADMIN.name())
.anyRequest().authenticated()
);
return http.build();
}
@Bean //setting authentication with user's username and password that configured in the userService class.
public DaoAuthenticationProvider daoAuthenticationProvider(UserService userService){
DaoAuthenticationProvider authenticationProvider= new DaoAuthenticationProvider();
authenticationProvider.setUserDetailsService(userService);
authenticationProvider.setPasswordEncoder(passwordEncoder());
return authenticationProvider;
}
}
I had the same problem also with the public endpoints before, but after i disabled the CSRF the problem got solved. At this point I don't know what I have to check next.
If you use Basic Auth you need add in your defaultSecurityFilterChain
If JWT: