RequestBodyAdvice
/**
* Allows customizing the request before its body is read and converted into an
* Object and also allows for processing of the resulting Object before it is
* passed into a controller method as an {@code @RequestBody} or an
* {@code HttpEntity} method argument.
*
* <p>Implementations of this contract may be registered directly with the
* {@code RequestMappingHandlerAdapter} or more likely annotated with
* {@code @ControllerAdvice} in which case they are auto-detected.
*
* @author Rossen Stoyanchev
* @since 4.2
*/
public interface RequestBodyAdvice {
/**
* 拦截器的匹配条件
*
* @param methodParameter
* @param targetType
* @param converterType
* @return
*/
boolean supports(MethodParameter methodParameter, Type targetType,
Class<? extends HttpMessageConverter<?>> converterType);
/**
* 在读取和转换请求正文之前第二次调用
*
* @param inputMessage 请求参数 - 目标方法参数
* @param parameter
* @param targetType 目标类型,不一定与方法参数类型相同,例如对于HttpEntity<String>
* @param converterType 用于反序列化主体的转换器
* @return
* @throws IOException 异常
*/
HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter,
Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException;
}
- 通过源码可以看出
- 在请求接收之前,进行接收并转换,可以用于请求加密
RequestBodyAdvice实现请求解密
/**
* 请求参数解密
*/
@Component
@RestControllerAdvice
// @RestControllerAdvice(basePackages = {"com.*.controller", "com.*.config"})
public class DecryptRequestBodyAdvice implements RequestBodyAdvice {
/**
* 需要解密的请求
*/
@Override
public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
// 这里写需要解密的逻辑
return true;
}
/**
* 请求解密处理
*/
@Override
public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> selectedConverterType) throws IOException {
HttpInputMessage ret = null;
String body = StreamUtils.copyToString(inputMessage.getBody(), StandardCharsets.UTF_8);
if (!StringUtils.isEmpty(body)) {
String decryptData = AESUtil.decrypt("解密密码", body);
if (!StringUtils.isEmpty(decryptData)) {
ByteArrayInputStream is = new ByteArrayInputStream(decryptData.getBytes());
ret = new MappingJacksonInputMessage(is, inputMessage.getHeaders());
is.close();
} else {
throw new IOException(ErrorCodeEnum.COMMON_REQUEST_BODY_DECRYPT_ERROR.getMessage());
}
}
return ret;
}
@Override
public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
return body;
}
@Override
public Object handleEmptyBody(@Nullable Object var1, HttpInputMessage var2, MethodParameter var3, Type var4, Class<? extends HttpMessageConverter<?>> var5) {
return var1;
}
}
解密方法
/**
* 解密
*
* @param key
* @param data
* @return
*/
public String decrypt(String key, String data) {
if (key == null) {
log.error("aes key不能为空,aes key:{}", key);
}
if (key.length() != 16) {
log.error("aes key长度必须为16位 ,aes key:{}", key);
}
try {
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] encrypted1 = Base64.getDecoder().decode(data);//先用base64解密
byte[] original = cipher.doFinal(encrypted1);
String originalString = new String(original, "utf-8");
return originalString;
} catch (IllegalArgumentException | NoSuchPaddingException | IllegalBlockSizeException | BadPaddingException | NoSuchAlgorithmException | InvalidKeyException | UnsupportedEncodingException e) {
log.warn("数据解密失败", e);
}
return null;
}
ResponseBodyAdvice
/**
* Allows customizing the response after the execution of an {@code @ResponseBody}
* or a {@code ResponseEntity} controller method but before the body is written
* with an {@code HttpMessageConverter}.
*
* <p>Implementations may be registered directly with
* {@code RequestMappingHandlerAdapter} and {@code ExceptionHandlerExceptionResolver}
* or more likely annotated with {@code @ControllerAdvice} in which case they
* will be auto-detected by both.
*
* @param <T> the body type
* @author Rossen Stoyanchev
* @since 4.1
*/
public interface ResponseBodyAdvice<T> {
/**
* 拦截器的匹配条件
*
* @param returnType
* @param converterType
* @return
*/
boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType);
/**
* @param body 返回体
* @param returnType 方法返回类型
* @param selectedContentType
* @param selectedConverterType
* @param request
* @param response
* @return
*/
@Nullable
T beforeBodyWrite(@Nullable T body, MethodParameter returnType, MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType,
ServerHttpRequest request, ServerHttpResponse response);
}
- 通过源码可以看出
- 在请求返回之前,进行接收并转换,可以用于返回加密
ResponseBodyAdvice实现返回加解密
/**
* 返回数据加密
*/
@Component
@RestControllerAdvice
public class EncryResponseBodyAdvice implements ResponseBodyAdvice<Object> {
@Override
public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> converterType) {
return true;
}
@SneakyThrows
@Override
public Object beforeBodyWrite(Object obj, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest serverHttpRequest,
ServerHttpResponse serverHttpResponse) {
if (obj != null) {
// 返回转json
String result = JsonUtil.toJson(obj);
if (!StringUtils.isEmpty(result)) {
try {
serverHttpResponse.getHeaders().set("content-type", MediaType.TEXT_PLAIN_VALUE);
OutputStream stream = serverHttpResponse.getBody();
stream.write(AESUtil.encrypt(properties.getEncry().getSecret(), result).getBytes("utf-8"));
stream.flush();
} catch (IOException e) {
}
}
}
return null;
}
}
java方法
/**
* 加密
*
* @param key
* @param data
* @return
*/
public String encrypt(String key, String data) {
if (key == null) {
log.error("aes key不能为空,aes key:{}", key);
}
if (key.length() != 16) {
log.error("aes key长度必须为16位 ,aes key:{}", key);
}
try {
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");//"算法/模式/补码方式"
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(data.getBytes("utf-8"));
return Base64.getEncoder().encodeToString(encrypted);
} catch (NoSuchPaddingException | IllegalBlockSizeException | BadPaddingException | NoSuchAlgorithmException | InvalidKeyException | UnsupportedEncodingException e) {
log.warn("数据加密失败", e);
}
return null;
}