Spring Security configuration for user credentials without frontend

116 views Asked by At

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.

1

There are 1 answers

1
Wladimir Diskowski On

If you use Basic Auth you need add in your defaultSecurityFilterChain

http.httpBasic(withDefaults())

If JWT:

http.oauth2ResourceServer(...)