Spring boot api gives 403 forbidden error

Viewed 2355

I have a spring boot application with a mongo databse and spring security as a dependency. It has two services first one for authentication and second one for application resource (entities, services controllers). This is my config class in the authentication service:

@Configuration
@EnableWebSecurity
public class AuthServerSecurityConfig extends WebSecurityConfigurerAdapter {

@Override
@Bean
protected UserDetailsService userDetailsService() {
    return new MongoUserDetailsService();
}

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(userDetailsService());
}

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.csrf().disable()
    .authorizeRequests().anyRequest().authenticated();
    System.out.println("auth");
}

@Override
public void configure(WebSecurity web) throws Exception {
    super.configure(web);
}

@Bean(name="authenticationManager")
@Lazy
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
    return super.authenticationManagerBean();
}
}

this is the rest controller:

@CrossOrigin(origins = "*", maxAge = 3600)
@RestController
@RequestMapping(value = "/api/users")
public class UserController {

@Autowired
UserServiceImpl userServiceImpl;

//Getting all users
@GetMapping(value = "")
public List<UserDTO> getAllUsers() {
    return userServiceImpl.getAllUsers();
    
}

//Getting a user by ID
@GetMapping(value = "/profil/{userId}")
public UserDTO getUserById(@PathVariable String userId) {
    return userServiceImpl.getUserById(userId);
}

//Getting a user by Username
@GetMapping(value = "/profil/username/{username}")
public UserDTO getUserByUsernameOrEmail(String username) {
    return userServiceImpl.getUserByUsernameOrEmail(username);
}

//Logout user and delete token
@PostMapping("/logout")
public void logout(HttpServletRequest request) {
     userServiceImpl.logout(request);
    
}

I changed my configure method to this :

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
    .csrf().disable()
    .authorizeRequests() // authorize
    .anyRequest().authenticated() // all requests are authenticated
    .and()
    .httpBasic();

    http.cors();
    
}

Now i get 401 unauthorized when acceccing protected resources.The problem is now even when i send the correct bearer token in the request header i still get 401 unauthorized "Full authentication is required to access this resource"

Update: I changed my project architecture from microservices to one simple spring boot project. this is the new code of the class "AuthServerSecurityConfig"

@Configuration
public class AuthServerSecurityConfig extends WebSecurityConfigurerAdapter {

@Override
@Bean
protected UserDetailsService userDetailsService() {
    return new MongoUserDetailsService();
}

@Autowired
BCryptPasswordEncoder passwordEncoder;

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    //auth.userDetailsService(userDetailsService());
    auth.userDetailsService(userDetailsService()).passwordEncoder(passwordEncoder);
}

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
    .csrf().disable()
    .anonymous().disable()
    .authorizeRequests()
    .antMatchers("/oauth/token").permitAll().and()
    .httpBasic();

    http.cors();
}

@Override
public void configure(WebSecurity web) throws Exception {
    super.configure(web);
}

@Bean(name="authenticationManager")
@Lazy
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
    return super.authenticationManagerBean();
}
}

and this "ResourceServerConfig" code:

@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {


@Autowired private ResourceServerTokenServices tokenServices;

@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
    resources.resourceId("foo").tokenServices(tokenServices);
}

@Override
public void configure(HttpSecurity http) throws Exception {
    
        http
        .authorizeRequests() // authorize
        .antMatchers("/oauth/**").permitAll();
        
        http
        .authorizeRequests().antMatchers("/api/**").authenticated();
        
        http
        .headers().addHeaderWriter(new HeaderWriter() {
        @Override
        public void writeHeaders(HttpServletRequest request, HttpServletResponse response) {
            response.addHeader("Access-Control-Allow-Origin", "*");
            if (request.getMethod().equals("OPTIONS")) {
                response.setHeader("Access-Control-Allow-Methods", request.getHeader("Access-Control-Request-Method"));
                response.setHeader("Access-Control-Allow-Headers", request.getHeader("Access-Control-Request-Headers"));
            }
        }
    });
}
}

When i try to access protected resource i get "error": "unauthorized", "error_description": "Full authentication is required to access this resource", Which is the normal behaviour.The problem is now i can't login to get the user aceess_token.

I get "401 unauthorized" when accessing this endpoint "http://localhost:8080/oauth/token?grant_type=password&username=user&password=user".

These are the default init user credentials and the user exists in my mongodatabase with a crypted and correct format password starts with "$2a" and has "60" caracters.

I get in "Encoded password does not look like BCrypt Authentication failed: password does not match stored value" in the console when trying to login.

3 Answers

In ResourceServerConfig class file, in the configure method change to the below code.

http
    .csrf().disable()
    .anonymous().disable()
    .authorizeRequests()
    .antMatchers("/oauth/token").permitAll().and()
    .httpBasic();

Let me know if it worked.

Here's an example of configuration spring security with a jwt token check : you can change the data source from h2 to mongodb and find the filters and providers used in my repo :

https://github.com/e2rabi/sbs-user-management/tree/main/sbs-user-management



@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true)
@FieldDefaults(level = PRIVATE, makeFinal = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private static final RequestMatcher PUBLIC_URLS = new OrRequestMatcher(
             // Put your public API here 
            new AntPathRequestMatcher("/public/**"),
            new AntPathRequestMatcher("/h2-console/**"),

    );
    private static final RequestMatcher PROTECTED_URLS = new NegatedRequestMatcher(PUBLIC_URLS);

    TokenAuthenticationProvider provider;

    SecurityConfig(final TokenAuthenticationProvider provider) {
        super();
        this.provider = requireNonNull(provider);
    }


   @Override
    protected void configure(final AuthenticationManagerBuilder auth) {
        auth.authenticationProvider(provider);
    }
    @Override
    public void configure(final WebSecurity web) {
        web.ignoring().requestMatchers(PUBLIC_URLS);
    }

    @Override
    protected void configure(final HttpSecurity http) throws Exception {
        http
                .sessionManagement()
                .sessionCreationPolicy(STATELESS)
                .and()
                .exceptionHandling()
                // this entry point handles when you request a protected page and you are not yet
                // authenticated
                .defaultAuthenticationEntryPointFor(forbiddenEntryPoint(), PROTECTED_URLS)
                .and()
                .authenticationProvider(provider)
                .addFilterBefore(restAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
                .authorizeRequests()
                .requestMatchers(PROTECTED_URLS)
                .authenticated()
                .and()
                .csrf().disable()
                .cors().disable()
                .formLogin().disable()
                .httpBasic().disable()
                .logout().disable();

                // h2 console config
                http.headers().frameOptions().sameOrigin();
                // disable page caching
                http.headers().cacheControl();
    }

    @Bean
    TokenAuthenticationFilter restAuthenticationFilter() throws Exception {
        final TokenAuthenticationFilter filter = new TokenAuthenticationFilter(PROTECTED_URLS);
        filter.setAuthenticationManager(authenticationManager());
        filter.setAuthenticationSuccessHandler(successHandler());
        return filter;
    }

    @Bean
    SimpleUrlAuthenticationSuccessHandler successHandler() {
        final SimpleUrlAuthenticationSuccessHandler successHandler = new SimpleUrlAuthenticationSuccessHandler();
        successHandler.setRedirectStrategy(new NoRedirectStrategy());
        return successHandler;
    }

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

    @Bean
    FilterRegistrationBean disableAutoRegistration(final TokenAuthenticationFilter filter) {
        final FilterRegistrationBean registration = new FilterRegistrationBean(filter);
        registration.setEnabled(false);
        return registration;
    }

    @Bean
    AuthenticationEntryPoint forbiddenEntryPoint() {
        return new HttpStatusEntryPoint(FORBIDDEN);
    }
}

Hope that my answer can help, you can drop a breakpoint to the line change the response status, and then check who and why it returns 403, it can finally help you get the solution

  1. Drop a breakpoint on the line set the 403 status, to see how this happen from the stackframes.

Guess that it returns 403 without much other information, but it must need to set the status to the response, right? So drop a breakpoint to the setStatus method, I don't know where it should locate, in tomcat lib, spring lib, or servlet lib. Check the HttpResponse, they're several implementation, set the breakpoints for those setStatus/setCode methods. (Next you can see it acutally happens at HttpResponseWrapper::setStatus)

  1. Analyze the stackframes to see what's going on there

please check https://stackoverflow.com/a/73577697/4033979

Related