Spring Boot中如何捕获ResponseEntityExceptionHandler响应生成JWE?
解决方案:让异常响应也经过JWE加密处理
这个问题我之前也碰到过,核心原因是ResponseEntityExceptionHandler(也就是你的ExceptionResolver)返回的响应,默认情况下可能没被CryptoResponseBodyAdvice正确拦截到。下面给你几个实用的解决方案,你可以根据自己的代码结构来选:
方案一:调整CryptoResponseBodyAdvice的拦截范围与执行顺序
默认的ResponseBodyAdvice可能只针对控制器的@ResponseBody方法生效,我们可以修改它的supports方法,让它覆盖所有ResponseEntity类型的返回(包括异常处理器生成的),同时通过@Order确保它优先执行。
修改后的CryptoResponseBodyAdvice示例:
@ControllerAdvice @Order(Ordered.HIGHEST_PRECEDENCE) // 确保这个Advice最先执行 public class CryptoResponseBodyAdvice implements ResponseBodyAdvice<Object> { // 注入你的JWE加密服务(如果有的话) private final JweEncryptionService encryptionService; public CryptoResponseBodyAdvice(JweEncryptionService encryptionService) { this.encryptionService = encryptionService; } @Override public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) { // 拦截所有ResponseEntity类型,以及带@ResponseBody注解的方法返回 return ResponseEntity.class.isAssignableFrom(returnType.getParameterType()) || returnType.hasMethodAnnotation(ResponseBody.class); } @Override public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { // 处理ResponseEntity类型的响应(异常处理器返回的就是这种) if (body instanceof ResponseEntity<?>) { ResponseEntity<?> responseEntity = (ResponseEntity<?>) body; Object actualBody = responseEntity.getBody(); if (actualBody == null) { return responseEntity; // 如果body为空,直接返回 } // 加密响应体 String encryptedBody = encryptionService.encryptToJWE(actualBody); // 重新包装成ResponseEntity返回 return new ResponseEntity<>(encryptedBody, responseEntity.getHeaders(), responseEntity.getStatusCode()); } // 处理普通@ResponseBody的响应 return encryptionService.encryptToJWE(body); } }
这里的关键是:
@Order(Ordered.HIGHEST_PRECEDENCE)确保加密逻辑在其他Advice之前执行supports方法明确包含ResponseEntity类型,覆盖异常处理器的返回
方案二:在异常处理器内部直接调用加密逻辑
如果不想修改ResponseBodyAdvice,可以直接在你的ExceptionResolver里,生成异常响应后先加密再返回。这种方式更直接,不需要依赖Advice的拦截逻辑。
修改后的ExceptionResolver示例:
@ControllerAdvice public class ExceptionResolver extends ResponseEntityExceptionHandler { private final JweEncryptionService encryptionService; public ExceptionResolver(JweEncryptionService encryptionService) { this.encryptionService = encryptionService; } @Override protected ResponseEntity<Object> handleExceptionInternal(Exception ex, Object body, HttpHeaders headers, HttpStatus status, WebRequest request) { // 先调用父类方法获取原始异常响应体 ResponseEntity<Object> originalResponse = super.handleExceptionInternal(ex, body, headers, status, request); Object originalBody = originalResponse.getBody(); if (originalBody == null) { return originalResponse; } // 加密响应体 String encryptedBody = encryptionService.encryptToJWE(originalBody); // 返回加密后的响应 return new ResponseEntity<>(encryptedBody, originalResponse.getHeaders(), originalResponse.getStatusCode()); } // 如果你重写了其他handleXXXException方法,也需要在这些方法里添加加密逻辑 // 比如handleMethodArgumentNotValid等 }
这种方式的好处是逻辑更清晰,异常响应的加密完全由你自己控制,不用担心Advice的拦截优先级问题。
方案三:自定义全局异常处理器替代ResponseEntityExceptionHandler
如果你的异常处理逻辑不需要依赖ResponseEntityExceptionHandler的默认实现,可以自己写一个全局异常处理器,直接在里面生成加密后的响应:
@ControllerAdvice public class CustomExceptionHandler { private final JweEncryptionService encryptionService; public CustomExceptionHandler(JweEncryptionService encryptionService) { this.encryptionService = encryptionService; } // 处理所有Exception类型的异常 @ExceptionHandler(Exception.class) public ResponseEntity<String> handleAllExceptions(Exception ex, WebRequest request) { // 构建自定义异常响应体 ErrorResponse errorResponse = new ErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR.value(), ex.getMessage()); // 加密响应体 String encryptedBody = encryptionService.encryptToJWE(errorResponse); return new ResponseEntity<>(encryptedBody, HttpStatus.INTERNAL_SERVER_ERROR); } // 可以添加更多针对特定异常的处理方法,比如@ExceptionHandler(MethodArgumentNotValidException.class) }
这种方式最灵活,完全摆脱了ResponseEntityExceptionHandler的限制,所有异常响应都能被加密处理。
注意事项
- 不管用哪种方案,都要确保你的加密服务能正确序列化响应体(比如用Jackson把对象转成JSON,再进行JWE加密)
- 如果响应体是
String类型(比如直接返回的错误信息),要注意加密逻辑的兼容性,避免重复加密或者序列化出错
内容的提问来源于stack exchange,提问作者Ashish Garg
相关产品推荐
相关产品推荐

