• Springboot项目如何加密返回值和请求参数


    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


    前言

    本公司项目要求越来越严格,并且其中一条为加密,要求请求参数和返回值加密。今天简单的记录


    一、encrypt-spring-boot-starter

    加密传输使用前端加密,后端解密的方式。后端加密前端解密的方式。并且加密后不能被普通工具解密,需要使用密钥进行解密,保证数据的安全。那么现在encrypt-spring-boot-starter可以直接使用。

    二、自定义加密方式

    1.为什么要自定义加密方式

    查看该依赖的源码,发现,只有使用依赖自定的res工具类包装返回值,并且他支持加密方式为AES,并不满足我们项目使用SM2的要求,同时,无法自定义加密方式。所以选择根据他的代码进行改造

    2.改造后的代码

    首先,需要知道,我们通过在接口上进行注解的方式,来定义改接口是否使用加密和解密。那么首先定义加密和解密的注解

    //加密
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.METHOD,ElementType.PARAMETER})
    public @interface Decrypt {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    //解密
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface Encrypt {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    使用aop进行切面解密和加密

    package com.hisw.talent.encrypt;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.context.properties.EnableConfigurationProperties;
    import org.springframework.core.MethodParameter;
    import org.springframework.http.HttpHeaders;
    import org.springframework.http.HttpInputMessage;
    import org.springframework.http.converter.HttpMessageConverter;
    import org.springframework.web.bind.annotation.ControllerAdvice;
    import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdviceAdapter;
    import java.io.ByteArrayInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.lang.reflect.Type;
    
    /**
     * @description:接口解密
     * @date 2022/11/25 14:03
     */
    @EnableConfigurationProperties(EncryptProperties.class)
    @ControllerAdvice
    public class DecryptRequest extends RequestBodyAdviceAdapter {
    
        @Autowired
        EncryptProperties encryptProperties;  
    
        public DecryptRequest() {
        }
    
        public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
            return methodParameter.hasMethodAnnotation(Decrypt.class) || methodParameter.hasParameterAnnotation(Decrypt.class);
        }
    
        public HttpInputMessage beforeBodyRead(final HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
            byte[] body = new byte[inputMessage.getBody().available()];
            inputMessage.getBody().read(body);
            try {
            	//this.encryptProperties.getKey() 这个key 根据你使用的加密方式来自定义AES需要满足16位,当然
            	//AESUtils.decrypt 可以替换为你需要的加密工具类,比如使用SM2,或者RSA非对称加密
                byte[] decrypt = AESUtils.decrypt(body, this.encryptProperties.getKey().getBytes());
                final ByteArrayInputStream bais = new ByteArrayInputStream(decrypt);
                return new HttpInputMessage() {
                    public InputStream getBody() throws IOException {
                        return bais;
                    }
                    public HttpHeaders getHeaders() {
                        return inputMessage.getHeaders();
                    }
                };
            } catch (Exception var8) {
                var8.printStackTrace();
                return super.beforeBodyRead(inputMessage, parameter, targetType, converterType);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    package com.hisw.talent.encrypt;
    
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.hisw.talent.common.utils.Result;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.context.properties.EnableConfigurationProperties;
    import org.springframework.core.MethodParameter;
    import org.springframework.http.MediaType;
    import org.springframework.http.converter.HttpMessageConverter;
    import org.springframework.http.server.ServerHttpRequest;
    import org.springframework.http.server.ServerHttpResponse;
    import org.springframework.web.bind.annotation.ControllerAdvice;
    import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
    
    /**
     * @description:接口加密
     * @date 2022/11/25 14:02
     */
    @EnableConfigurationProperties(EncryptProperties.class)
    @ControllerAdvice
    public class EncryptResponse implements ResponseBodyAdvice<Result> {
        private ObjectMapper om = new ObjectMapper();
    
        @Autowired
        EncryptProperties encryptProperties;
    
        public EncryptResponse() {
        }
    
        public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
            return returnType.hasMethodAnnotation(Encrypt.class);
        }
    
        public Result beforeBodyWrite(Result body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
            byte[] keyBytes = this.encryptProperties.getKey().getBytes();
            try {
    /*            if (body.getMsg() != null) {
                    body.setMsg(org.javaboy.encrypt.starter.utils.AESUtils.encrypt(body.getMsg().getBytes(), keyBytes));
                }*/
                if (body.getData() != null) {
                	//这个步骤可以替换为你的工具类 ,同时 Result 返回工具类替换为你项目中的
                    body.setData(AESUtils.encrypt(this.om.writeValueAsBytes(body.getData()), keyBytes));
                }
            } catch (Exception var9) {
                var9.printStackTrace();
            }
    
            return body;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    最后,在你controller 里 打上 注解来控制接口的加密和解密


    总结

    方式为切面编程,难度不大,但是目前有个问题,AES加密 一旦 body数据太大,那么解密时会出现失败的情况,提示长度需要为16的倍数。RSA加密boby太长,需要使用分段解密。同时会造成前端解密后出现乱码的情况。SM2加密,同样会遇到解密失败的情况。需要04开头,但是过程中依然会报错。希望知道的同学解惑

  • 相关阅读:
    web中间件漏洞-weblogic漏洞-弱口令war包上传
    抽奖基本算法讨论
    Zookeeper客户端——I0Itec-zkClient
    光谱异常样本检测分析
    无胁科技-TVD每日漏洞情报-2022-7-29
    赢麻了!smardaten闷声干大事,竟然用无代码开发了复杂小程序!
    报表工具,应该适用哪种部署方式?_光点科技
    前端开发:JS中常用事件汇总
    (四) ES6 新特性 —— 参数默认值与spread扩展运算符
    鹅厂练习 13 年 Coding 后,我悟了
  • 原文地址:https://blog.csdn.net/qq_48329942/article/details/128143604