基于 RequestBodyAdvice 與 ResponseBodyAdvice 實(shí)現(xiàn)統(tǒng)一加密和解密
在日常開(kāi)發(fā)中,有時(shí)候經(jīng)常需要和第三方接口打交道,有時(shí)候是我方調(diào)用別人的第三方接口,有時(shí)候是別人在調(diào)用我方的第三方接口,那么為了調(diào)用接口的安全性,一般都會(huì)對(duì)傳輸?shù)臄?shù)據(jù)進(jìn)行加密操作,如果每個(gè)接口都由我們自己去手動(dòng)加密和解密,那么工作量太大而且代碼冗余。那么有沒(méi)有簡(jiǎn)單的方法,借助 spring 提供的 RequestBodyAdvice 和 ResponseBodyAdvice 可以實(shí)現(xiàn)解密和加密操作。
需求:
后臺(tái)方法上如果有 @Encrypt 注解和 @RequestBody 修飾的方法,需要進(jìn)行參數(shù)的解密
后臺(tái)方法上如果有 @Encrypt 注解和 @ResponseBody 修飾的方法,需要進(jìn)行參數(shù)的加密
加密和解密規(guī)則
加密:對(duì)返回的值中增加 -encrypt 值
解密:對(duì)傳入的值中刪除 -encrypt 值
注:
@Encrypt 為自定義的一個(gè)注解。
此處為了簡(jiǎn)單,就使用刪除或增加 -encrypt 這個(gè),實(shí)際情況下可以使用復(fù)雜的加解密規(guī)則
0x01: 基本思路介紹
RequestBodyAdvice:在 sping 4.2 新加入的一個(gè)接口,它可以使用在 @RequestBody 或 HttpEntity 修改的參數(shù)之前進(jìn)行參數(shù)的處理,比如進(jìn)行參數(shù)的解密。
ResponseBodyAdvice:在 spring 4.1 新加入的一個(gè)接口,在消息體被 HttpMessageConverter 寫入之前允許 Controller 中 @ResponseBody 修飾的方法或 ResponseEntity 調(diào)整響應(yīng)中的內(nèi)容,比如進(jìn)行相應(yīng)的加密。
0x02: 功能實(shí)現(xiàn)步驟
1. 編寫加密注解類(Encrypt)
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Encrypt {
} 2. 編寫RequestBodyAdvice接口實(shí)現(xiàn)類,實(shí)現(xiàn)數(shù)據(jù)的解密操作
@Slf4j
@RestControllerAdvice
public class ParamEncryptRequestBodyAdvice implements RequestBodyAdvice {
@Override
public boolean supports(MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
return methodParameter.hasParameterAnnotation(RequestBody.class);
}
@Override
public Object handleEmptyBody(Object o, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
return o;
}
@Override
public HttpInputMessage beforeBodyRead(HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) throws IOException {
return new HttpInputMessage() {
@Override
public InputStream getBody() throws IOException {
log.info("此處進(jìn)行解密數(shù)據(jù)");
return new ByteArrayInputStream(IOUtils.toString(httpInputMessage.getBody()).replace("-encrypt", "").getBytes(StandardCharsets.UTF_8));
}
@Override
public HttpHeaders getHeaders() {
return httpInputMessage.getHeaders();
}
};
}
@Override
public Object afterBodyRead(Object o, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
return o;
}
} 3. 編寫ResponseBodyAdvice接口實(shí)現(xiàn)類,實(shí)現(xiàn)數(shù)據(jù)的加密操作
@Slf4j
@RestControllerAdvice
public class ParamEncryptResponseBodyAdvice implements ResponseBodyAdvice {
private final ObjectMapper objectMapper = new ObjectMapper();
@Override
public boolean supports(MethodParameter returnType, Class converterType) {
return returnType.hasMethodAnnotation(ResponseBody.class);
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
log.info("此處進(jìn)行加密數(shù)據(jù)");
if (null != body) {
try {
Map map = objectMapper.readValue(objectMapper.writeValueAsString(body), Map.class);
map.forEach((key, value) -> map.put(key, value + "-encrypt"));
return map;
} catch (IOException e) {
log.error("加密數(shù)據(jù)失敗.", e);
}
}
return body;
}
} 4. 編寫控制層進(jìn)行測(cè)試
@RestController
@RequestMapping("user-info")
@Slf4j
public class UserInfoController {
/**
* 添加用戶實(shí)現(xiàn)返回值加密
*
* @param userInfo
* @return
*/
@PostMapping("add")
@Encrypt
public UserInfo add(@RequestBody UserInfo userInfo) {
log.info("添加新用戶:[{}]", userInfo);
return userInfo;
}
/**
* 修改實(shí)現(xiàn)獲取的參數(shù)進(jìn)行解密
*
* @param userInfo
* @return
*/
@PostMapping("update")
public UserInfo update(@Encrypt @RequestBody UserInfo userInfo) {
log.info("修改用戶信息:[{}]", userInfo);
return userInfo;
}
} 5. 測(cè)試參數(shù)的解密操作

可以看到:參數(shù)中的 -encrypt 傳遞后后臺(tái)被后臺(tái)自動(dòng)截取了,這樣就類似于解密操作。
6. 測(cè)試返回值的加密操作
可以看到:返回的值后面都有一個(gè) -encrypt, 這樣就實(shí)現(xiàn)了類似于加密操作。

喜歡,在看
