求助:如何自定义SpringBoot 2 OAuth2的异常响应格式
To standardize your exception responses to the desired format ({"status": "error", "message": "..."}), we’ll handle three key cases: OAuth2 unauthorized errors, invalid/expired token errors, and 404 Not Found errors. Here’s a step-by-step implementation:
1. Create a Custom Error Response DTO
First, define a simple class to represent your uniform error structure:
import com.fasterxml.jackson.annotation.JsonInclude; @JsonInclude(JsonInclude.Include.NON_NULL) public class CustomErrorResponse { private String status; private String message; public CustomErrorResponse(String status, String message) { this.status = status; this.message = message; } // Getters and setters (or use Lombok's @Data for brevity) public String getStatus() { return status; } public void setStatus(String status) { this.status = status; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } }
2. Customize OAuth2 Authentication Entry Point
This handles the unauthorized and invalid_token errors (like expired tokens). Extend Spring’s OAuth2AuthenticationEntryPoint to override the response format:
import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.security.core.AuthenticationException; import org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint; import org.springframework.stereotype.Component; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @Component public class CustomOAuth2AuthenticationEntryPoint extends OAuth2AuthenticationEntryPoint { @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { response.setContentType("application/json"); response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); // Customize error messages for specific cases String errorMessage = authException.getMessage(); if (errorMessage.contains("Access token expired")) { errorMessage = "Access token has expired. Please obtain a new token."; } else if (errorMessage.contains("Full authentication is required")) { errorMessage = "Full authentication is required to access this resource."; } CustomErrorResponse errorResponse = new CustomErrorResponse("error", errorMessage); new ObjectMapper().writeValue(response.getOutputStream(), errorResponse); } }
3. Register the Custom Entry Point in Resource Server Config
Update your resource server configuration to use the custom entry point:
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter; import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer; @Configuration @EnableResourceServer public class ResourceServerConfig extends ResourceServerConfigurerAdapter { @Autowired private CustomOAuth2AuthenticationEntryPoint customOAuth2AuthenticationEntryPoint; @Override public void configure(ResourceServerSecurityConfigurer resources) throws Exception { resources.authenticationEntryPoint(customOAuth2AuthenticationEntryPoint); } @Override public void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .anyRequest().authenticated() .and() .exceptionHandling() .authenticationEntryPoint(customOAuth2AuthenticationEntryPoint); } }
4. Handle 404 Not Found Errors
Use a @ControllerAdvice to catch Spring MVC’s NoHandlerFoundException and format it to your desired structure:
import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.servlet.NoHandlerFoundException; @ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(NoHandlerFoundException.class) public ResponseEntity<CustomErrorResponse> handleNotFoundError(NoHandlerFoundException ex) { CustomErrorResponse errorResponse = new CustomErrorResponse("error", "The requested resource was not found."); return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND); } // Optional: Handle AccessDeniedException for forbidden requests @ExceptionHandler(org.springframework.security.access.AccessDeniedException.class) public ResponseEntity<CustomErrorResponse> handleAccessDeniedError(org.springframework.security.access.AccessDeniedException ex) { CustomErrorResponse errorResponse = new CustomErrorResponse("error", "You do not have permission to access this resource."); return new ResponseEntity<>(errorResponse, HttpStatus.FORBIDDEN); } }
5. Enable 404 Exception Throwing
Add these properties to your application.properties (or application.yml) to make Spring Boot throw NoHandlerFoundException when a path isn’t mapped:
spring.mvc.throw-exception-if-no-handler-found=true spring.web.resources.add-mappings=false
Testing the Changes
- When accessing a protected resource without authentication, you’ll get:
{"status": "error", "message": "Full authentication is required to access this resource."} - When using an expired token:
{"status": "error", "message": "Access token has expired. Please obtain a new token."} - When accessing a non-existent path:
{"status": "error", "message": "The requested resource was not found."}
This setup ensures all your exception responses follow the same custom format, making it easier for client applications to handle errors consistently.
内容的提问来源于stack exchange,提问作者MAHIE

