Tôi đang phát triển dịch vụ REST. Nó sử dụng JSON và phải trả về một số đối tượng JSON được xác định trước trong trường hợp có vấn đề. Phản hồi Mùa xuân mặc định trông giống như sau:Spring MVC (hoặc Spring Boot). Phản hồi JSON tùy chỉnh cho các ngoại lệ liên quan đến bảo mật như 401 trái phép hoặc 403 bị cấm)
{
"timestamp": 1512578593776,
"status": 403,
"error": "Forbidden",
"message": "Access Denied",
"path": "/swagger-ui.html"
}
Tôi muốn thay thế JSON mặc định này bằng JSON riêng (với ngăn xếp và thông tin liên quan đến ngoại lệ bổ sung).
Mùa xuân cung cấp cách tiện dụng để ghi đè hành vi mặc định. Người ta phải định nghĩa một bean @RestControllerAdvice
với một trình xử lý ngoại lệ tùy chỉnh. Như thế này
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(value = {Exception.class})
public ResponseEntity<ExceptionResponse> unknownException(Exception ex) {
ExceptionResponse resp = new ExceptionResponse(ex, level); // my custom response object
return new ResponseEntity<ExceptionResponse>(resp, resp.getStatus());
}
@ExceptionHandler(value = {AuthenticationException.class})
public ResponseEntity<ExceptionResponse> authenticationException(AuthenticationExceptionex) {
// WON'T WORK
}
}
Sau đó, đối tượng tùy chỉnh ExceptionResponse
sẽ được chuyển đổi thành JSON bằng Spring.
VẤN ĐỀ LÀ, ngoại lệ bảo mật như InsufficientAuthenticationException
không thể bị chặn bởi phương thức được chú thích là @ExceptionHandler
. Loại ngoại lệ này xảy ra trước khi servlet điều phối Spring MVC được nhập vào và tất cả các trình xử lý MVC đã được khởi tạo.
Có thể chặn ngoại lệ này bằng bộ lọc tùy chỉnh và tạo một chuỗi tuần tự JSON từ đầu. Trong trường hợp này, người ta sẽ nhận được một mã hoàn toàn độc lập với phần còn lại của cơ sở hạ tầng Spring MVC. Nó không tốt.
Giải pháp tôi thấy có vẻ hiệu quả, nhưng có vẻ điên rồ.
@Configuration
public class CustomSecurityConfiguration extends
WebSecurityConfigurerAdapter {
@Autowired
protected RequestMappingHandlerAdapter requestMappingHandlerAdapter;
@Autowired
protected GlobalExceptionHandler exceptionHandler;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest()
.fullyAuthenticated();
http.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint());
}
public AuthenticationEntryPoint authenticationEntryPoint() {
return new AuthenticationEntryPoint() {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException, ServletException {
try {
ResponseEntity<ExceptionResponse> objResponse = exceptionHandler.authenticationException(authException);
Method unknownException = exceptionHandler.getClass().getMethod("authenticationException", AuthenticationException.class);
HandlerMethod handlerMethod = new HandlerMethod(exceptionHandler, unknownException);
MethodParameter returnType = handlerMethod.getReturnValueType(objResponse);
ModelAndViewContainer mvc = new ModelAndViewContainer(); // not really used here.
List<HttpMessageConverter<?>> mconverters = requestMappingHandlerAdapter.getMessageConverters();
DispatcherServletWebRequest webRequest = new DispatcherServletWebRequest(request, response);
HttpEntityMethodProcessor processor = new HttpEntityMethodProcessor(mconverters);
processor.handleReturnValue(objResponse, returnType, mvc, webRequest);
} catch (IOException e) {
throw e;
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new ServletException(e);
}
}
};
}
Có cách nào để sử dụng ống nối tiếp Spring (với Spring build trong bộ chuyển đổi tin nhắn, đàm phán định dạng MIME, v.v.) trông đẹp hơn này?
Đây là giải pháp thanh lịch hơn một chút, rất giống với giải pháp của bạn nhưng bao gồm ngoại lệ ban đầu thành một ngoại lệ cho phép bị "nắm bắt" bởi @ExceptionHandler: https://stackoverflow.com/questions/43632565/exceptionhandler-for- a-pre-controller-filter-spring-security/43636386 – spekdrum
http://www.baeldung.com/spring-security-custom-access-denied-page –