• 【RuoYi-Vue-Plus】学习笔记 50 - 集成 JSEncrypt 实现请求加密传输(源码)


    前言

    在开始本文内容前,首先说一声:大家节日快乐!

    本文来简单分析一下框架中请求加密的实现方式。该功能来源于框架 pr!377,后续进行了优化并手动合并到了框架 5.X 分支,并在版本 v5.1.0 正式发布。

    在这里插入图片描述

    PR 详情(传送门):

    在这里插入图片描述

    下面来看下具体的实现方式。

    框架版本

    前端

    • jsencrypt:3.3.1
    • crypto-js:^4.1.1

    package.json

    在这里插入图片描述

    服务端

    1. 框架版本:v5.1.0
    2. Hutool 工具包(hutool-crypto):5.8.22

    框架集成

    在上面的 PR 详情里面已经描述了具体的实现方式,下面分别对前端后端进行说明,前端部分由于是小白,所以只把相关的代码贴出来。

    前端集成

    1、总览

    实现思路:

    1. 随机生成密钥
    2. 对密钥进行 Base64 编码,并使用 RSA 公钥加密密钥,加入请求头
    3. 使用密钥对请求体进行 AES 加密

    在这里插入图片描述

    2、代码实现

    请求拦截器
    request.ts

    在这里插入图片描述

    随机生成AES密钥:
    generateAesKey

    在这里插入图片描述

    对AES密钥进行Base64编码:
    encryptBase64

    在这里插入图片描述

    编码后使用 RSA 公钥加密:
    .env.development

    在这里插入图片描述

    RSA 公私钥应由服务端定义,前端持有公钥对数据加密,且两端公钥需要保持一致。

    jsencrypt.ts

    在这里插入图片描述

    使用AES密钥对请求体进行加密:
    crypto.ts

    在这里插入图片描述

    服务端集成

    1、总览

    实现思路:

    1. 编写 yaml 配置信息,生成公私钥
    2. 编写配置类
    3. 编写过滤器实现解密逻辑
    4. 配置过滤器

    加密模块 ruoyi-common-encrypt

    在这里插入图片描述

    2、代码实现

    2.1、配置信息 application.yml

    在这里插入图片描述

    2.2、配置类 ApiDecryptProperties

    在这里插入图片描述

    2.3、过滤器 CryptoFilter

    在这里插入图片描述

    2.4、包装类 DecryptRequestBodyWrapper

    在这里插入图片描述

    对于请求数据的解密主要在这个方法中实现。

    完整代码:

    package org.dromara.common.encrypt.filter;
    
    import cn.hutool.core.io.IoUtil;
    import jakarta.servlet.ReadListener;
    import jakarta.servlet.ServletInputStream;
    import jakarta.servlet.http.HttpServletRequest;
    import jakarta.servlet.http.HttpServletRequestWrapper;
    import org.dromara.common.core.constant.Constants;
    import org.dromara.common.encrypt.utils.EncryptUtils;
    import org.springframework.http.MediaType;
    
    import java.io.BufferedReader;
    import java.io.ByteArrayInputStream;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.nio.charset.StandardCharsets;
    
    /**
     * 解密请求参数工具类
     *
     * @author wdhcr
     */
    public class DecryptRequestBodyWrapper extends HttpServletRequestWrapper {
    
        private final byte[] body;
    
        public DecryptRequestBodyWrapper(HttpServletRequest request, String publicKey, String privateKey, String headerFlag) throws IOException {
            super(request);
            // 获取 AES 密码 采用 RSA 加密
            String headerRsa = request.getHeader(headerFlag);
            String decryptAes = EncryptUtils.decryptByRsa(headerRsa, privateKey);
            // 解密 AES 密码
            String aesPassword = EncryptUtils.decryptByBase64(decryptAes);
            request.setCharacterEncoding(Constants.UTF8);
            byte[] readBytes = IoUtil.readBytes(request.getInputStream(), false);
            String requestBody = new String(readBytes, StandardCharsets.UTF_8);
            // 解密 body 采用 AES 加密
            String decryptBody = EncryptUtils.decryptByAes(requestBody, aesPassword);
            body = decryptBody.getBytes(StandardCharsets.UTF_8);
        }
    
        @Override
        public BufferedReader getReader() {
            return new BufferedReader(new InputStreamReader(getInputStream()));
        }
    
    
        @Override
        public int getContentLength() {
            return body.length;
        }
    
        @Override
        public long getContentLengthLong() {
            return body.length;
        }
    
        @Override
        public String getContentType() {
            return MediaType.APPLICATION_JSON_VALUE;
        }
    
    
        @Override
        public ServletInputStream getInputStream() {
            final ByteArrayInputStream bais = new ByteArrayInputStream(body);
            return new ServletInputStream() {
                @Override
                public int read() {
                    return bais.read();
                }
    
                @Override
                public int available() {
                    return body.length;
                }
    
                @Override
                public boolean isFinished() {
                    return false;
                }
    
                @Override
                public boolean isReady() {
                    return false;
                }
    
                @Override
                public void setReadListener(ReadListener readListener) {
    
                }
            };
        }
    }
    
    
    • 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
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95

    2.5、加解密工具类 EncryptUtils

    EncryptUtils#decryptByRsa

    在这里插入图片描述

    EncryptUtils#decryptByBase64

    在这里插入图片描述

    EncryptUtils#decryptByAes

    在这里插入图片描述

    2.6、自动装配 ApiDecryptAutoConfiguration

    在这里插入图片描述

    在这里插入图片描述

    请求测试

    1、测试方法

    登录: [POST] /auth/login

    2、输出

    对解密信息输出一下:

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    3、Debug

    (跟上面步骤 2 的请求不是同一次请求,因此密钥不同,加密解密数据不同)

    在这里插入图片描述

    在这里插入图片描述

    (完)

  • 相关阅读:
    Flink之窗口指派API模板
    【YOLOv5】实现扑克牌的点数识别
    利用union判断CPU大小端以及大小端转换(C++)
    c#Nettonsoft.net库常用的方法json序列化反序列化
    HMS Core音频编辑服务音源分离与空间音频渲染,助力快速进入3D音频的世界
    5G NR下行载波波形生成-Matlab
    文件对象的常用方法和属性
    高级篇之ENC2-V2编码器的RTSP另一个妙用(长地址转换为短地址)
    Linux运维实战:CentOS7.6操作系统从入门到精通(11-15)
    服务器又被挖矿记录
  • 原文地址:https://blog.csdn.net/Michelle_Zhong/article/details/134018557