Spring Boot:返回 403 而不是 405

问题描述 投票:0回答:0

我有一个 Spring Boot 应用程序,它是安全性的一部分,我在 ApiSecurityConfig 类中有这个方法:

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
              .csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
              .and()
              .exceptionHandling().authenticationEntryPoint(authEntryPoint)
              .and()
              .authorizeHttpRequests()
              .antMatchers(
                    "/h2-console/**",
                               "/swagger-resources/**",
                               "/v2/api-docs",
                               "/swagger-ui/**",
                               "/auth/**",
                               "/user",
                               "/csrf")
              .permitAll()
              .anyRequest()
              .authenticated()
              .and()
              .sessionManagement()
              .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
              .and()
              .authenticationProvider(authenticationProvider)
              .headers(headers -> headers.frameOptions().disable())
              .csrf(csrf -> csrf.ignoringAntMatchers("/h2-console/**", "/swagger-ui/**"))
              .addFilterBefore(malformedTokenHandler, LogoutFilter.class)
              .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);

        return http.build();
}

我需要启用 CSRF 所以我没有使用

http
  .csrf().disable();

我还使用一个类来处理这样的异常:

public class RestExceptionHandlingController {

    private final HttpServletRequest request;
    private final MessageSource messageSource;

    public RestExceptionHandlingController(HttpServletRequest request,
                                           MessageSource messageSource) {
        this.request = request;
        this.messageSource = messageSource;
    }

    
    private ResponseEntity<Object> buildResponseEntity(
          Exception exception, ExceptionResponseObject exceptionResponseObject) {

        if (exception != null) {
            exception.printStackTrace();
            log.error("Error Message: {}", exception.getMessage());
        }

        return new ResponseEntity<>(exceptionResponseObject, exceptionResponseObject.getStatus());
    }

    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<?> handleValidationErrors(MethodArgumentNotValidException exception, Locale locale) {
        String methodName = "handlerValidateException";

        log.error("{} -> Error fields: {}", methodName, exception.getCause(), exception);

        List<ValidationError> validationErrors = new ArrayList<>();

        exception.getBindingResult().getAllErrors().forEach((error) -> {

            String object = error.getObjectName();
            String field = ((FieldError) error).getField();
            Object rejectedValue = ((FieldError) error).getRejectedValue();
            String errorMessage;

            try {
                errorMessage = messageSource.getMessage(
                      (Objects.requireNonNull(error.getDefaultMessage())), null, locale);
            } catch (Exception e) {
                errorMessage = error.getDefaultMessage();
            }

            validationErrors.add(new ValidationError(object, field, rejectedValue, errorMessage));

            log.error("Validation Error:"
                        + " Path {},"
                        + " Object {},"
                        + " Field {}, "
                        + " Rejected Value {},"
                        + " Message: {}",
                  request.getRequestURI(), object, field, rejectedValue, errorMessage);
        });

        return ResponseEntity.status(HttpStatus.BAD_REQUEST)
              .body(new ExceptionResponseObject(HttpStatus.BAD_REQUEST, "Validation Errors", validationErrors));
    }

    @ExceptionHandler(BadRequestException.class)
    public ResponseEntity<?> handleBadRequest(BadRequestException exception) {

        ExceptionResponseObject exceptionResponseObject =
              new ExceptionResponseObject(HttpStatus.BAD_REQUEST, exception.getMessage(),
                    exception.getValidationErrors());

        return buildResponseEntity(exception, exceptionResponseObject);
    }

    @ExceptionHandler(ConflictException.class)
    public ResponseEntity<?> handleConflict(ConflictException exception) {

        ExceptionResponseObject exceptionResponseObject =
              new ExceptionResponseObject(HttpStatus.CONFLICT, exception.getMessage(), exception.getValidationErrors());

        return buildResponseEntity(exception, exceptionResponseObject);
    }

    @ExceptionHandler(NotFoundException.class)
    public ResponseEntity<?> handleNotFound(NotFoundException exception) {

        ExceptionResponseObject exceptionResponseObject =
              new ExceptionResponseObject(HttpStatus.NOT_FOUND, exception.getMessage(),
                    exception.getValidationErrors());

        return buildResponseEntity(exception, exceptionResponseObject);
    }

    @ExceptionHandler(Exception.class)
    public ResponseEntity<Object> handleInternalServerError(Exception exception) {
        if (exception instanceof NullPointerException) {
            return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
        }

        ExceptionResponseObject exceptionResponseObject =
              new ExceptionResponseObject(HttpStatus.INTERNAL_SERVER_ERROR, exception.getMessage());

        return buildResponseEntity(exception, exceptionResponseObject);
    }

    
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public ResponseEntity<Object> handleMissingRequestBody(HttpMessageNotReadableException exception) {
        return new ResponseEntity<>("Required request body is missing", HttpStatus.BAD_REQUEST);
    }


    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public ResponseEntity<Object> handleHttpRequestMethodNotSupportedException(
          HttpRequestMethodNotSupportedException exception) {
        return new ResponseEntity<>("Method not supported", HttpStatus.NOT_IMPLEMENTED);
    }

    @ExceptionHandler(ServiceUnavailableException.class)
    public ResponseEntity<Object> handleServiceUnavailableException(ServiceUnavailableException exception) {
        ExceptionResponseObject exceptionResponseObject =
              new ExceptionResponseObject(HttpStatus.SERVICE_UNAVAILABLE, exception.getMessage());

        return buildResponseEntity(exception, exceptionResponseObject);
    }

    @ExceptionHandler(BadGatewayException.class)
    public ResponseEntity<Object> handleBadGatewayException(BadGatewayException ex) {
        log.error("Error handling Bad Gateway exception: {}", ex.getMessage());

        ExceptionResponseObject response = new ExceptionResponseObject(HttpStatus.BAD_GATEWAY, "Bad Gateway");

        return new ResponseEntity<>(response, HttpStatus.BAD_GATEWAY);
    }

    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    @ExceptionHandler(AuthenticationException.class)
    public ResponseEntity<?> handleAuthenticationException(AuthenticationException ex) {

        HttpStatus httpStatus = HttpStatus.UNAUTHORIZED;
        String message = ex.getLocalizedMessage();

        if (ex.getLocalizedMessage().equals("Full authentication is required to access this resource")) {
            httpStatus = HttpStatus.FORBIDDEN;
            message = "Missing Credentials!";
        }

        ExceptionResponseObject errorResponse = new ExceptionResponseObject(httpStatus,
              "AUTHENTICATION FAILED: " + message);
        return ResponseEntity.status(httpStatus).body(errorResponse);
    }

    @ResponseStatus(HttpStatus.FORBIDDEN)
    @ExceptionHandler(AccessDeniedException.class)
    public ResponseEntity<?> handleAccessDeniedException(AccessDeniedException exception) {
        ExceptionResponseObject exceptionResponseObject =
              new ExceptionResponseObject(HttpStatus.FORBIDDEN, exception.getMessage());

        return buildResponseEntity(exception, exceptionResponseObject);
    }

    @ExceptionHandler(ExpiredJwtException.class)
    public ResponseEntity<?> handleInvalidToken(ExpiredJwtException exception) {
        ExceptionResponseObject exceptionResponseObject =
              new ExceptionResponseObject(HttpStatus.FORBIDDEN, exception.getMessage());

        return buildResponseEntity(exception, exceptionResponseObject);
    }

    @ExceptionHandler(MalformedJwtException.class)
    public ResponseEntity<?> handleInvalidToken(MalformedJwtException exception) {
        ExceptionResponseObject exceptionResponseObject =
              new ExceptionResponseObject(HttpStatus.FORBIDDEN, exception.getMessage());

        return buildResponseEntity(exception, exceptionResponseObject);
    
    @ExceptionHandler(MethodNotAllowedException.class)
    public ResponseEntity<?> handleMethodNotAllowedException(MethodNotAllowedException 
    exception) {

        ExceptionResponseObject exceptionResponseObject =
              new ExceptionResponseObject(HttpStatus.METHOD_NOT_ALLOWED, "Method not 
              allowed.");

        return buildResponseEntity(exception, exceptionResponseObject);
    }
}

我在 CustomAuthenticationEntryPoint 类中也有一个启动方法,如下所示:

@Override
    public void commence(HttpServletRequest request, HttpServletResponse response,
                         AuthenticationException authException)
          throws IOException, ServletException {
        response.setStatus(HttpStatus.METHOD_NOT_ALLOWED.value());
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        response.getWriter().write("{\"message\": \"Method not allowed\"}");
    }

所以当我尝试在邮递员中测试端点时,例如调用 POST 而不是 GET 抛出 403 Forbidden 而不是 405 Method Not Allowed。那么如何解决呢?

spring-boot security exception http-status-code-403 http-status-code-405
© www.soinside.com 2019 - 2024. All rights reserved.