使用ControllerAdvice进行接口加解密处理

注意:以下代码为伪代码,仅提供思路
使用条件:
接口入参:所有接口必须使用POST请求,且需包含@RequestBody注解
接口出参:无固定要求,根据实际情况修改以下代码

@ControllerAdvice
public class AppControllerAdvice implements RequestBodyAdvice {
    @Override
    public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
    //保证所有接口都走向该Advice
        return true;
    }

    @Override
    public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
        try {
        //这里通过注解值获取使用哪种解密方式
            if (parameter.getMethod().isAnnotationPresent(ApiEncrypt.class)) {
                ApiEncrypt serializedField = parameter.getMethodAnnotation(ApiEncrypt.class);
                if (serializedField.withBox()) {
                    //使用RSA解密
                    return new DecryptedInputMessage(inputMessage, "RSA");
                } else {
                    //使用AES解密
                    return new DecryptedInputMessage(inputMessage, "AES");
                }

            }
            return inputMessage;
        } catch (Exception e) {
            e.printStackTrace();
            return inputMessage;
        }
    }

    @Override
    public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        return body;
    }

    @Override
    public Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        return body;
    }


    static class DecryptedInputMessage implements HttpInputMessage {
        private final HttpHeaders headers;
        private final InputStream body;

        public DecryptedInputMessage(HttpInputMessage inputMessage, String encryptType) throws Exception {
            this.headers = inputMessage.getHeaders();
            String privateKey = "你的RSA私钥";
            if (Objects.equals(encryptType, "RSA")) {
                this.body = IOUtils.toInputStream(new String(RSA.decrypt(RSA.hexToPrivateKey(privateKey), Hex.decode(IOUtils.toString(inputMessage.getBody(),"utf-8"))), StandardCharsets.UTF_8), "utf-8");
            } else {
                this.body = IOUtils.toInputStream(new String(AES.decrypt(Hex.decode(IOUtils.toString(inputMessage.getBody(), StandardCharsets.UTF_8)), "1234567890123456")), "utf-8");
            }
        }

        @Override
        public InputStream getBody() throws IOException {
            return body;
        }

        @Override
        public HttpHeaders getHeaders() {
            return headers;
        }

    }


}

出参加密处理

@ControllerAdvice
//这里的param最好就是用Object,因为加密后的不再是某个返回类,而是String
public class AppResponseBodyAdvice implements ResponseBodyAdvice<Object> {
    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        return true;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        ApiEncrypt serializedField = returnType.getMethodAnnotation(ApiEncrypt.class);
        if(serializedField == null){
            return body;
        }
        if(serializedField.withBox()){
            String publicKey = "RSA公钥";
            return "RSA"+ new String(Hex.encode(RSA.encrypt(RSA.hexToPublicKey(publicKey), JSON.toJSONBytes(body))));
        }else{
            return "AES"+new String(Hex.encode(AES.encrypt(JSON.toJSONBytes(body),"AES秘钥")));

        }

    }
}

如果只这样用,当你的返回类不是String的时候,接口会多出两个双引号,对前端及其他同行不友好,所以还要处理一下

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
//自己搞一个MessageConvert,让他能解析掉加密后的String
    public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
        return new MappingJackson2HttpMessageConverter() {

            @Override
            protected void writeInternal(Object object, Type type, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
                if (object instanceof String) {
                    Charset charset = StandardCharsets.UTF_8;
                    StreamUtils.copy((String) object, charset, outputMessage.getBody());
                } else {
                    super.writeInternal(object, type, outputMessage);
                }
            }
        };
    }

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        MappingJackson2HttpMessageConverter converter = mappingJackson2HttpMessageConverter();
        converter.setSupportedMediaTypes(new LinkedList<MediaType>() {{
            add(MediaType.APPLICATION_JSON_UTF8);
        }});
        //这里是去掉双引号的精髓,把String的处理器和自定义处理器的排序提前,保证解析的时候先走这两个
        converters.add(0,new StringHttpMessageConverter());
        converters.add(1,converter);
    }

}

这里再提一下为啥会多出来两个双引号,是因为正常不加密的情况下,你的返回类型是Response<>这种,他是一个json方法,但是你在responseAdvice的时候,把他强转成了string,但是那个时候,你的处理器已经被spring确定为了json,而在json中,字符串就是要加双引号的,他就自动给你加上了, 这也是为什么要去重写那个writeInternal的原因,然后具体的原理什么的,如果想不起来了就看一下这个
springMVC中框架对HttpMessageConverter的选择原理
然后源码是在
org.springframework.web.selvet.mvc.method.annotation.AbstractMessageConverterMethodProcessorwriteWithMessageConverters方法中,可以进来了之后直接看213行左右,或者搜这个

genericConverter = converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter)converter : null;

  • 9
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值