Search code examples
javaspring-bootspring-securitybearer-token

Spring Boot Security - Get 403 instead of 401 without using bearer token


I have a problem to handle with the exception handling to get 401 Unauthorized result when I don't use bearer token through Postman.

When I use a user bearer token and send a request to this method, I get this response message shown below

{
    "time": "2023-05-29T18:06:23.381897147",
    "httpStatus": "UNAUTHORIZED",
    "header": "AUTH ERROR",
    "isSuccess": false
}

I want to get this message shown below as it is a user not admin

{
    "time": "2023-05-29T18:06:23.381897147",
    "httpStatus": "FORBIDDEN",
    "header": "AUTH ERROR",
    "isSuccess": false
}

Moreover, I don't use any bearer token, It always returns 403 Forbidden instead of 401 Unauthorized. I selected No Auth from type part in Authorization in Postman and then send a request to the endpoint, handleAccessDeniedError cannot be called. I think a different exception is used for that. Which exception should be used?

Here is the relevant part of GlobalExceptionHandler.

@ExceptionHandler({AccessDeniedException.class})
    protected ResponseEntity<Object> handleAccessDeniedError(final AccessDeniedException exception) {
        log.error(exception.getMessage(), exception);

        Error error = Error.builder()
                .httpStatus(HttpStatus.FORBIDDEN)
                .header(Error.Header.AUTH_ERROR.getName())
                .build();
        return new ResponseEntity<>(error, HttpStatus.FORBIDDEN);
    }

Here is the sample code snippets

@GetMapping
@PreAuthorize("hasAnyAuthority('ADMIN')")
public Response<?> getUsers(@RequestBody @Valid UserListRequest listRequest) {

}

How can I fix the issue?


Solution

  • Here is the solution shown below

    1 ) Define CustomAuthenticationEntryPoint

    @Component
    public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
    
        /**
         * Handles the unauthorized request by sending an "Unauthorized"
         * response with the HTTP status code 401 (SC_UNAUTHORIZED).
         *
         * @param request       the {@link HttpServletRequest} object representing the incoming request
         * @param response      the {@link HttpServletResponse} object used to send the response
         * @param authException the {@link AuthenticationException} that occurred during authentication
         * @throws IOException if an I/O error occurs while sending the response
         */
        @Override
        public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
        }
    
    }
    

    2 ) Inject CustomAuthenticationEntryPoint class to SecurityConfiguration

    @Configuration
    @EnableWebSecurity
    @EnableGlobalAuthentication
    @EnableMethodSecurity
    @RequiredArgsConstructor
    class SecurityConfiguration {
    
    ....
    
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity httpSecurity,
    AuthenticationFilter authenticationFilter, CustomAuthenticationEntryPoint customAuthenticationEntryPoint){
    
    httpSecurity.exceptionHandling()
                    .authenticationEntryPoint(customAuthenticationEntryPoint);
    
    } 
    
    .....
    
    }