我有一个 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。那么如何解决呢?