当前位置: 首页 > news >正文

若依plus请求加解密

若依plus请求加解密

本文将以 RuoYi Plus 框架 为例,详细介绍如何通过 RSA + AES 混合加密机制 实现接口的请求加密与响应加密,确保数据安全传输。

整个加密通信分为两个阶段:

  • 请求加密解密:前端加密 → 后端解密
  • 响应加密解密:后端加密 → 前端解密

这种模式采用 对称加密 + 非对称加密混合方案

加密方式使用场景特点
AES(对称加密)加密请求体、响应体加解密速度快,但需要安全地传递密钥
RSA(非对称加密)加密 AES 密钥密钥安全性高,但性能较低,适合加密小数据

因此,整个设计遵循以下原则:

用 RSA 保护 AES 密钥,用 AES 加密真实数据。

1.请求加密解密流程

前端加密流程

在前端(如 Vue / React)发起请求时,执行以下步骤:

  1. 随机生成 32 位的 AES 密钥 aesKey
  2. aesKey 进行 Base64 编码 得到 base64Aes
  3. 使用 RSA 公钥base64Aes 加密,得到 rsaAes
  4. rsaAes 放入请求头中:encrypt-key: rsaAes
  5. 使用 aesKey 对请求体 JSON 进行 AES 加密;
  6. 将加密后的密文作为请求 body 发送至后端。

后端解密流程

后端接收到请求后,反向解密流程如下:

  1. 从 Header 中读取 encrypt-key
  2. 使用 RSA 私钥 解密得到 base64Aes
  3. base64Aes 进行 Base64 解码得到真实的 aesKey
  4. 使用该 aesKey 对请求体(AES 密文)进行解密;
  5. 得到明文 JSON 请求参数。
┌─────────────────────────────┐
│          前端 Vue/React     │
│─────────────────────────────│
│ 1. 随机生成 AES 密钥(32位) aesKey
│ 2. 将 aesKey Base64 编码 → base64Aes
│ 3. 使用 RSA 公钥 加密 base64Aes → rsaAes
│ 4. 把 rsaAes 放到请求头:encrypt-key
│ 5. 使用 aesKey 对请求体(JSON)进行 AES 加密
│ 6. 发送 HTTP 请求(body 为 AES密文)→ 后端
└─────────────────────────────┘│▼
┌─────────────────────────────┐
│           后端 API           │
│─────────────────────────────│
│ 1. 从 header 获取 rsaAes
│ 2. 使用 RSA 私钥 解密 → base64Aes
│ 3. Base64 解码 → aesKey
│ 4. 使用 aesKey 解密请求体(AES)
│ 5. 得到明文 JSON 参数
└─────────────────────────────┘

2.响应加密解密流程

后端加密流程

  1. 生成一个新的 32 位 AES 密钥;
  2. 对密钥进行 Base64 编码;
  3. 使用 RSA 公钥加密后放入响应头:encrypt-key
  4. 使用该 AES 密钥加密响应内容;
  5. 返回加密后的响应体。

前端解密流程

  1. 从响应头读取 encrypt-key
  2. 使用 RSA 私钥解密 → base64Aes
  3. Base64 解码 → aesKey
  4. 使用 aesKey 对响应体进行 AES 解密;
  5. 得到明文 JSON 响应。
┌─────────────────────────────┐
│           后端 API           │
│─────────────────────────────│
│ 1. 生成新的 AES 密钥(32位) aesKey
│ 2. Base64 编码 → base64Aes
│ 3. 使用 RSA 公钥 加密 → rsaAes
│ 4. 响应头设置 encrypt-key = rsaAes
│ 5. 使用 aesKey 对响应内容进行 AES 加密
│ 6. 将 AES 密文作为响应 body 返回
└─────────────────────────────┘│▼
┌─────────────────────────────┐
│          前端 Vue/React     │
│─────────────────────────────│
│ 1. 从响应头读取 encrypt-key (rsaAes)2. 使用 RSA 私钥 解密 → base64Aes
│ 3. Base64 解码 → aesKey
│ 4. 使用 aesKey 解密响应体(AES)
│ 5. 得到明文 JSON 响应
└─────────────────────────────┘

3.请求解密包装器

核心逻辑:

  • 从请求头中读取加密的 encrypt-key
  • 使用 RSA 私钥解密得到 AES 密钥;
  • 读取原始请求流;
  • 使用 AES 密钥解密请求体;
  • 将解密后的内容重新写入请求流,供后续 Filter/Controller 使用。
public class DecryptRequestBodyWrapper extends HttpServletRequestWrapper {private final byte[] body;public DecryptRequestBodyWrapper(HttpServletRequest request, 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);}@Overridepublic BufferedReader getReader() {return new BufferedReader(new InputStreamReader(getInputStream()));}@Overridepublic int getContentLength() {return body.length;}@Overridepublic long getContentLengthLong() {return body.length;}@Overridepublic String getContentType() {return MediaType.APPLICATION_JSON_VALUE;}@Overridepublic ServletInputStream getInputStream() {final ByteArrayInputStream bais = new ByteArrayInputStream(body);return new ServletInputStream() {@Overridepublic int read() {return bais.read();}@Overridepublic int available() {return body.length;}@Overridepublic boolean isFinished() {return false;}@Overridepublic boolean isReady() {return false;}@Overridepublic void setReadListener(ReadListener readListener) {}};}
}

4.响应加密包装器

核心逻辑:

  • 拦截输出流,暂存在 ByteArrayOutputStream
  • 生成新的 AES 密钥;
  • 对密钥 Base64 编码后用 RSA 公钥加密;
  • 将加密密钥写入响应头;
  • 使用 AES 加密原始响应内容;
  • 将加密后的结果写回给客户端。
public class EncryptResponseBodyWrapper extends HttpServletResponseWrapper {private final ByteArrayOutputStream byteArrayOutputStream;private final ServletOutputStream servletOutputStream;private final PrintWriter printWriter;public EncryptResponseBodyWrapper(HttpServletResponse response) throws IOException {super(response);this.byteArrayOutputStream = new ByteArrayOutputStream();this.servletOutputStream = this.getOutputStream();this.printWriter = new PrintWriter(new OutputStreamWriter(byteArrayOutputStream));}@Overridepublic PrintWriter getWriter() {return printWriter;}@Overridepublic void flushBuffer() throws IOException {if (servletOutputStream != null) {servletOutputStream.flush();}if (printWriter != null) {printWriter.flush();}}@Overridepublic void reset() {byteArrayOutputStream.reset();}public byte[] getResponseData() throws IOException {flushBuffer();return byteArrayOutputStream.toByteArray();}public String getContent() throws IOException {flushBuffer();return byteArrayOutputStream.toString();}/*** 获取加密内容** @param servletResponse response* @param publicKey       RSA公钥 (用于加密 AES 秘钥)* @param headerFlag      请求头标志* @return 加密内容* @throws IOException*/public String getEncryptContent(HttpServletResponse servletResponse, String publicKey, String headerFlag) throws IOException {// 生成秘钥String aesPassword = RandomUtil.randomString(32);// 秘钥使用 Base64 编码String encryptAes = EncryptUtils.encryptByBase64(aesPassword);// Rsa 公钥加密 Base64 编码String encryptPassword = EncryptUtils.encryptByRsa(encryptAes, publicKey);// 设置响应头// vue版本需要设置servletResponse.addHeader("Access-Control-Expose-Headers", headerFlag);servletResponse.setHeader("Access-Control-Allow-Origin", "*");servletResponse.setHeader("Access-Control-Allow-Methods", "*");servletResponse.setHeader(headerFlag, encryptPassword);servletResponse.setCharacterEncoding(StandardCharsets.UTF_8.toString());// 获取原始内容String originalBody = this.getContent();// 对内容进行加密return EncryptUtils.encryptByAes(originalBody, aesPassword);}@Overridepublic ServletOutputStream getOutputStream() throws IOException {return new ServletOutputStream() {@Overridepublic boolean isReady() {return false;}@Overridepublic void setWriteListener(WriteListener writeListener) {}@Overridepublic void write(int b) throws IOException {byteArrayOutputStream.write(b);}@Overridepublic void write(byte[] b) throws IOException {byteArrayOutputStream.write(b);}@Overridepublic void write(byte[] b, int off, int len) throws IOException {byteArrayOutputStream.write(b, off, len);}};}}

5加解密过滤器

  1. 读取请求头中的 encrypt-key
  2. 若存在 → 使用 DecryptRequestBodyWrapper 解密请求体
  3. 检查 Controller 方法上是否有 @ApiEncrypt(response=true)
  4. 若存在 → 使用 EncryptResponseBodyWrapper 包装响应
  5. 放行过滤链
  6. Controller 执行完毕后,对响应内容进行加密输出
public class CryptoFilter implements Filter {private final ApiDecryptProperties properties;public CryptoFilter(ApiDecryptProperties properties) {this.properties = properties;}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {HttpServletRequest servletRequest = (HttpServletRequest) request;HttpServletResponse servletResponse = (HttpServletResponse) response;// 获取加密注解ApiEncrypt apiEncrypt = this.getApiEncryptAnnotation(servletRequest);boolean responseFlag = apiEncrypt != null && apiEncrypt.response();ServletRequest requestWrapper = null;ServletResponse responseWrapper = null;EncryptResponseBodyWrapper responseBodyWrapper = null;// 是否为 put 或者 post 请求if (HttpMethod.PUT.matches(servletRequest.getMethod()) || HttpMethod.POST.matches(servletRequest.getMethod())) {// 是否存在加密标头String headerValue = servletRequest.getHeader(properties.getHeaderFlag());if (StringUtils.isNotBlank(headerValue)) {// 请求解密requestWrapper = new DecryptRequestBodyWrapper(servletRequest, properties.getPrivateKey(), properties.getHeaderFlag());} else {// 是否有注解,有就报错,没有放行if (ObjectUtil.isNotNull(apiEncrypt)) {HandlerExceptionResolver exceptionResolver = SpringUtils.getBean("handlerExceptionResolver", HandlerExceptionResolver.class);exceptionResolver.resolveException(servletRequest, servletResponse, null,new ServiceException("没有访问权限,请联系管理员授权", HttpStatus.FORBIDDEN));return;}}}// 判断是否响应加密if (responseFlag) {responseBodyWrapper = new EncryptResponseBodyWrapper(servletResponse);responseWrapper = responseBodyWrapper;}chain.doFilter(ObjectUtil.defaultIfNull(requestWrapper, request),ObjectUtil.defaultIfNull(responseWrapper, response));if (responseFlag) {servletResponse.reset();// 对原始内容加密String encryptContent = responseBodyWrapper.getEncryptContent(servletResponse, properties.getPublicKey(), properties.getHeaderFlag());// 对加密后的内容写出servletResponse.getWriter().write(encryptContent);}}/*** 获取 ApiEncrypt 注解*/private ApiEncrypt getApiEncryptAnnotation(HttpServletRequest servletRequest) {RequestMappingHandlerMapping handlerMapping = SpringUtils.getBean("requestMappingHandlerMapping", RequestMappingHandlerMapping.class);// 获取注解try {HandlerExecutionChain mappingHandler = handlerMapping.getHandler(servletRequest);if (ObjectUtil.isNotNull(mappingHandler)) {Object handler = mappingHandler.getHandler();if (ObjectUtil.isNotNull(handler)) {// 从handler获取注解if (handler instanceof HandlerMethod handlerMethod) {return handlerMethod.getMethodAnnotation(ApiEncrypt.class);}}}} catch (Exception e) {return null;}return null;}@Overridepublic void destroy() {}
}
http://www.dtcms.com/a/601353.html

相关文章:

  • PHP Filter:深入了解其功能与实现
  • Linux基础指令(简易版)
  • 农田灌区监测设备:赋能现代农业的精准感知与智能调控
  • 中山 灯饰 骏域网站建设专家百度关键词推广帝搜软件
  • 自己怎么做 优惠券网站西京一师一优课建设网站
  • CST电动车EMC仿真(二)——电机控制器MCU的EMC仿真
  • WPP Media(群邑)DOOH 解决方案 重构数字户外广告价值
  • 基于SpringBoot+Vue的美容美发在线预约系统的设计与实现【附源码】
  • 数字化转型改变了什么?从技术底层到业务本质的深度重构
  • 从 “被动抢修” 到 “主动防控”,安科瑞 mini 智能微断,重构末端配电安全新逻辑
  • 从经验到算法:智能获客系统如何重构ToB销售效率
  • Oracle 19C 数据字典 DBA_HIST_SEG_STAT 详细说明
  • tsfile.raw提示
  • JAVA中六种策略模式的实现
  • 【ZeroRange WebRTC】TLS 底层原理与工作机制(深入解析)
  • 【论文阅读16】-LLM-TSFD:一种基于大型语言模型的工业时间序列人机回路故障诊断方法
  • 联想键盘失灵处理方法
  • 网站建设scyiyouhtml5模板之家
  • 做网站网络公司泉州住房建设局网站
  • 电子绕核运动为何不辐射能量、不坠入原子核?
  • RK3588核心板/开发板RT-Linux系统实时性及硬件中断延迟测试
  • 11. 函数极限
  • 死锁的本质:形成条件、检测机制与排查策略
  • Winform控件:RichTextBox
  • 大疆影石掰手腕,智能影像“跨界”内卷
  • 建设一个网站平台一款app的开发成本
  • 吴江城乡和住房建设局网站商务网站建设 模板
  • sparksql远程服务thriftserver.sh启停脚本
  • 非模板匹配目标识别算法
  • NLP基础(一)_简介