Search code examples
javaspringhttpspring-securityauthorization

Spring Security: returning 403 Forbidden for all requests after successful authentication


I wish to allow all users to login via JWT. Only logged in users are allowed to access the private endpoints. Nothing is being logged, not even as "Access Denied". Here is my SecurityConfig configuration class.

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
@EnableMethodSecurity
public class SecurityConfig {

    private final JwtAuthenticationFilter jwtAuthFilter;
    private final AuthenticationProvider authProvider;

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeHttpRequests()
                .requestMatchers(publicEndpoints()).permitAll()
                .requestMatchers(userEndpoints()).permitAll()
                .requestMatchers(adminEndpoints()).hasRole("ADMIN")
                .anyRequest().denyAll()
                .and()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authenticationProvider(authProvider)
                .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class)
                .exceptionHandling()
                .accessDeniedHandler((request, response, ex) -> {
                    System.out.println("Access Denied: " + ex.getMessage());
                    response.sendError(HttpServletResponse.SC_FORBIDDEN);
                });

        return http.build();
    }

    private RequestMatcher userEndpoints() {
        return new OrRequestMatcher(
                new AntPathRequestMatcher("/u/**")
        );
    }

    private RequestMatcher adminEndpoints() {
        return new OrRequestMatcher(
                new AntPathRequestMatcher("/admin/**")
        );
    }

    private RequestMatcher publicEndpoints() {
        return new OrRequestMatcher(
                new AntPathRequestMatcher("/user/register"),
                new AntPathRequestMatcher("/user/signin")
        );
    }
}  

Here is the JwtAuthenticationFilter:

@Component
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    private final JwtService jwtService;
    private final UserDetailsService userDetailsService;

    @Override
    protected void doFilterInternal(@NonNull HttpServletRequest request,
                                    @NonNull HttpServletResponse response,
                                    @NonNull FilterChain filterChain) throws ServletException, IOException {

        final String authHeader = request.getHeader("Authorization");

        if(authHeader == null || !authHeader.startsWith("Bearer ")) {
            filterChain.doFilter(request, response);
            return;
        }

        final String jwtToken = authHeader.substring(7); // removes "Bearer " string.
        final String username = jwtService.extractUsername(jwtToken);

        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
            UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);

            if (jwtService.isTokenValid(jwtToken, userDetails)) {
                UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
                        userDetails, null, userDetails.getAuthorities());

                authToken.setDetails(new WebAuthenticationDetailsSource()
                        .buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(authToken);
            }
        }
        filterChain.doFilter(request, response);
    }
}

Currently, with the above configuration, I am getting no logs whatsoever. I tried changing the security configuration to .requestMatchers(userEndpoints()).hasRole("USER") which still returned 403 Forbidden. But this time, there was a log saying "Access Denied: Access Denied".

I have also tried using @PreAuthorize("hasRole('USER')") for the controller method being called, but that also did nothing.


Solution

  • In the SecurityConfig class, inside the securityFilterChain method, you are using the "denyAll()" method. With it, you are saying that all the aforementioned requests should be denied. Change the "denyAll()" to "authenticated()".

    The method would be as follows:

     @Bean
        public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
            http.csrf().disable()
                    .authorizeHttpRequests()
                    .requestMatchers(publicEndpoints()).permitAll()
                    .requestMatchers(userEndpoints()).permitAll()
                    .requestMatchers(adminEndpoints()).hasRole("ADMIN")
                    .anyRequest().authenticated()
                    .and()
                    .sessionManagement()
                    .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                    .and()
                    .authenticationProvider(authProvider)
                    .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class)
                    .exceptionHandling()
                    .accessDeniedHandler((request, response, ex) -> {
                        System.out.println("Access Denied: " + ex.getMessage());
                        response.sendError(HttpServletResponse.SC_FORBIDDEN);
                    });
    
            return http.build();
        }