基于RequestBodyAdvice与ResponseBodyAdvice实现统一加密和解密

在日常开发中,有时候经常需要和第三方接口打交道,有时候是我方调用别人的第三方接口,有时候是别人在调用我方的第三方接口,那么为了调用接口的安全性,一般都会对传输的数据进行加密操作,如果每个接口都由我们自己去手动加密和解密,那么工作量太大而且代码冗余。那么有没有简单的方法,借助 spring 提供的 RequestBodyAdvice 和 ResponseBodyAdvice 可以实现解密和加密操作。

需求:

  • 后台方法上如果有 @Encrypt 注解和 @RequestBody 修饰的方法,需要进行参数的解密
  • 后台方法上如果有 @Encrypt 注解和 @ResponseBody 修饰的方法,需要进行参数的加密
  • 加密和解密规则

加密:对返回的值中增加 -encrypt 值

解密:对传入的值中删除 -encrypt 值

注:

  • @Encrypt 为自定义的一个注解。
  • 此处为了简单,就使用删除或增加 -encrypt 这个,实际情况下可以使用复杂的加解密规则

0x01: 基本思路介绍

RequestBodyAdvice:在 sping 4.2 新加入的一个接口,它可以使用在 @RequestBody 或 HttpEntity 修改的参数之前进行参数的处理,比如进行参数的解密。

ResponseBodyAdvice:在 spring 4.1 新加入的一个接口,在消息体被 HttpMessageConverter 写入之前允许 Controller 中 @ResponseBody 修饰的方法或 ResponseEntity 调整响应中的内容,比如进行相应的加密。

0x02: 功能实现步骤

1. 编写加密注解类(Encrypt)

@Target({ElementType.PARAMETER, ElementType.METHOD})  
@Retention(RetentionPolicy.RUNTIME)  
@Documented  
public @interface Encrypt {  

}  

2. 编写RequestBodyAdvice接口实现类,实现数据的解密操作

@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("此处进行解密数据");  
                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接口实现类,实现数据的加密操作

@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("此处进行加密数据");  
        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("加密数据失败.", e);  
            }  
        }  
        return body;  
    }  
}  

4. 编写控制层进行测试

@RestController  
@RequestMapping("user-info")  
@Slf4j  
public class UserInfoController {  

    /** 
     * 添加用户实现返回值加密 
     * 
     * @param userInfo 
     * @return 
     */  
    @PostMapping("add")  
    @Encrypt  
    public UserInfo add(@RequestBody UserInfo userInfo) {  
        log.info("添加新用户:[{}]", userInfo);  
        return userInfo;  
    }  

    /** 
     * 修改实现获取的参数进行解密 
     * 
     * @param userInfo 
     * @return 
     */  
    @PostMapping("update")  
    public UserInfo update(@Encrypt @RequestBody UserInfo userInfo) {  
        log.info("修改用户信息:[{}]", userInfo);  
        return userInfo;  
    }  
}  

5. 测试参数的解密操作

基于 RequestBodyAdvice 与 ResponseBodyAdvice 实现统一加密和解密

可以看到:参数中的 -encrypt 传递后后台被后台自动截取了,这样就类似于解密操作。

6. 测试返回值的加密操作

基于 RequestBodyAdvice 与 ResponseBodyAdvice 实现统一加密和解密

可以看到:返回的值后面都有一个 -encrypt, 这样就实现了类似于加密操作。

内容出处:,

声明:本网站所收集的部分公开资料来源于互联网,转载的目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。如果您发现网站上有侵犯您的知识产权的作品,请与我们取得联系,我们会及时修改或删除。文章链接:http://www.yixao.net/tech/23348.html

发表评论

登录后才能评论