SpringBoot 接口加解密全过程详解

一、接口为什么要加密

接口加密传输,主要作用:

当然不是说接口加密后,就能完完全全的保护我们的数据,但至少能防一部分人拿到我们的数据。
而且接口加密感觉逼格是不是高过一点!!!

二、加密思路

1、加密简介

加密算法有很多,在能加密又能解密的算法可分为:

2、加密流程

思路:
假设现在客户端是A,服务端是B,现在A要去B请求接口

虽然这样就实现了接口的加密方式,但是呢,非对称加密的加解密速度相比对称加密速度很慢,当传输的数据很大时就更加明显了。
所以我们对称与非对称一起用,理解上面的流程之后,我们在其基础稍微改下:

三、代码实现

1、自定义类

下面就来实现上面的两个接口实现类代码

EncryptRequestAdvice.java

@ControllerAdvice(basePackages = {"top.lrshuai.encrypt.controller"})
public class EncryptRequestAdvice implements RequestBodyAdvice {

    @Autowired
    private KeyConfig keyConfig;

    /**
     * 是否需要解码
     */
    private boolean isDecode;

    @Override
    public boolean supports(MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        // 方法或类上有注解
        if (Utils.hasMethodAnnotation(methodParameter,new Class[]{Encrypt.class,Decode.class})) {
            isDecode=true;
            // 这里返回true 才支持
            return true;
        }
        return false;
    }

    @Override
    public HttpInputMessage beforeBodyRead(HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) throws IOException {
        if(isDecode){
            return new DecodeInputMessage(httpInputMessage, keyConfig);
        }
        return httpInputMessage;
    }

    @Override
    public Object afterBodyRead(Object obj, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        // 这里就是已经读取到body了,obj就是
        return obj;
    }

    @Override
    public Object handleEmptyBody(Object obj, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        // body 为空的时候调用
        return obj;
    }

}

DecodeInputMessage.java

这个类就是具体的解码逻辑了

public class DecodeInputMessage implements HttpInputMessage {

    private HttpHeaders headers;

    private InputStream body;

    public DecodeInputMessage(HttpInputMessage httpInputMessage, KeyConfig keyConfig) {
        // 这里是body 读取之前的处理
        this.headers = httpInputMessage.getHeaders();
        String encodeAesKey = "";
        List keys = this.headers.get(Result.KEY);
        if (keys != null && keys.size() > 0) {
            encodeAesKey = keys.get(0);
        }
        try {
            // 1、解码得到aes 密钥
            String decodeAesKey = RsaUtils.decodeBase64ByPrivate(keyConfig.getRsaPrivateKey(), encodeAesKey);
            // 2、从inputStreamReader 得到aes 加密的内容
            String encodeAesContent = new BufferedReader(new InputStreamReader(httpInputMessage.getBody())).lines().collect(Collectors.joining(System.lineSeparator()));
            // 3、AES通过密钥CBC解码
            String aesDecode = AesUtils.decodeBase64(encodeAesContent, decodeAesKey, keyConfig.getAesIv().getBytes(), AesUtils.CIPHER_MODE_CBC_PKCS5PADDING);
            if (!StringUtils.isEmpty(aesDecode)) {
                // 4、重新写入到controller
                this.body = new ByteArrayInputStream(aesDecode.getBytes());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

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

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

EncryptResponseAdvice.java

@Slf4j
@ControllerAdvice(basePackages = {"top.lrshuai.encrypt.controller"})
public class EncryptResponseAdvice implements ResponseBodyAdvice {

    @Autowired
    private KeyConfig keyConfig;

    @Override
    public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
        // return true 有效
        return true;
    }

    /**
     * 返回结果加密
     * @param obj 接口返回的对象
     * @param methodParameter method
     * @param mediaType  mediaType
     * @param aClass HttpMessageConverter class
     * @param serverHttpRequest request
     * @param serverHttpResponse response
     * @return obj
     */
    @Override
    public Object beforeBodyWrite(Object obj, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
        // 方法或类上有注解
        if (Utils.hasMethodAnnotation(methodParameter,new Class[]{Encrypt.class, Encode.class})) {
            // 这里假设已经定义好返回的model就是Result
            if (obj instanceof Result) {
                try {
                    // 1、随机aes密钥
                    String randomAesKey = AesUtils.generateSecret(256);
                    // 2、数据体
                    Object data = ((Result) obj).getData();
                    // 3、转json字符串
                    String jsonString = JSON.toJSONString(data);
                    // 4、aes加密数据体
                    String aesEncode = AesUtils.encodeBase64(jsonString, randomAesKey,keyConfig.getAesIv().getBytes(),AesUtils.CIPHER_MODE_CBC_PKCS5PADDING);
                    // 5、重新设置数据体
                    ((Result) obj).put(Result.DATA,aesEncode);
                    // 6、使用前端的rsa公钥加密 aes密钥 返回给前端
                    ((Result) obj).put(Result.KEY,RsaUtils.encodeBase64PublicKey(keyConfig.getFrontRsaPublicKey(),randomAesKey));
                    // 7、返回
                    return obj;
                } catch (Exception e) {
                   log.error("加密失败:",e);
                }
            }
        }
        return obj;
    }
}

看代码注释,不说了。

2、加密工具类

加密工具类,我在网上收集整理了一下,搞了个jar。直接在pom.xml 引入即可。如下:


	top.lrshuai.encryption
	encryption-tools
	1.0.3

自此核心代码都讲完了,这里只是给出了个demo,可以参考一下(代码写的也不是很好,很多地方也没有封装),加密方式多种多样,都是可以自由更改,这种加密方式不喜欢就改。
差点忘记了,前端代码呢。

3、前端代码

前端也是在Github分别找了两个库:

然后我使用的是Vue写的简单页面(业余前端)

html




    
    请求

    



    
字段: Value:
userId:
userName:
age:
info:
AES密钥:
AES向量:

要发送的数据:{{parameter}}

加密后的数据:{{encodeContent}}


收到服务端的内容:{{result}}

解密服务端AES密钥内容:{{decodeAes}}

最终拿到服务端的内容:{{decodeContent}}

主要看testRequest() 这个方法就行了,都有代码注释。

注意点

最终效果

在上面的postman中

  • data:里面的数据就是aes加密后的数据
  • key:里面就是前端RSA公钥加密后的AES密钥(前端需要用私钥解密得到aes密钥,然后再用密钥解开data里面的数据)
  • status:这个是状态码,如果报错了就不是200,不然报错了返回的数据,前端解几百年都解不开。

4、源码地址

https://gitee.com/rstyro/spring-boot/tree/master/Springboot2-api-encrypt
  • 作者:rstyro
  • 来源: https://rstyro.github.io/blog/2020/10/22/Springboot2接口加解密全过程详解(含前端代码)/

页面更新:2024-03-12

标签:接口   密钥   注解   对称   服务端   详解   全过程   代码   方法   内容   数据   信息

1 2 3 4 5

上滑加载更多 ↓
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2008-2024 All Rights Reserved. Powered By bs178.com 闽ICP备11008920号-3
闽公网安备35020302034844号

Top