功能实现流程图:

参考:
链接: https://www.cnblogs.com/throwable/p/9471938.html.
链接: https://www.cnblogs.com/whcghost/p/5657594.html.
链接: https://blog.csdn.net/charry0110/article/details/109495035.
链接: https://www.cnblogs.com/elaron/archive/2013/04/09/3010375.html.
链接: https://blog.csdn.net/qq_35164962/article/details/102704880.

功能说明:

主要是为了实现对app的请求的参数和响应进行加密,本次只做了json格式参数的post请求的加解密。主要考虑到get参数加密后太长会超过url长度限制。上传下载的请求也没有加解密,主要担心加解密时间太长。
除了对参数和响应内容进行加密外,还进行了时间戳的过期检查操作和签名验证的操作,来防止伪造请求访问。但是只对时间戳拼接一个头的字符串进行签名,并没有使用参数内容进行签名,想想只要破解不了我的加密方式,我用时间戳来签名应该就够了。

主要实现代码

json请求接收器

package com.wx.security;import com.fasterxml.jackson.databind.ObjectMapper;
import com.wx.exception.BadRequestException;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJacksonInputMessage;
import org.springframework.util.StreamUtils;
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.lang.reflect.Type;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.util.Date;/****/
//@ControllerAdvice(basePackages = "com.wx.*.controller")
@ControllerAdvice
public class CustomRequestBodyAdvice extends RequestBodyAdviceAdapter {private final ObjectMapper objectMapper;public CustomRequestBodyAdvice(ObjectMapper objectMapper) {this.objectMapper = objectMapper;}@Overridepublic boolean supports(MethodParameter methodParameter, Type targetType,Class<? extends HttpMessageConverter<?>> converterType) {return RsaProperties.enSwitch;}@Overridepublic HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType,Class<? extends HttpMessageConverter<?>> converterType) throws IOException {String content = StreamUtils.copyToString(inputMessage.getBody(), Charset.forName("UTF-8"));EncryptModel in = objectMapper.readValue(content, EncryptModel.class);//先校验一下时间戳是否过期,如果时间戳和后台当前时间相差超过5分钟,则请求判定为失效if (Math.abs(in.getTimestamp() - new Date().getTime()) > RsaProperties.deadlineMins * 1000 * 60) {throw new BadRequestException("请求已过期!");}String inRawSign = in.getTimestamp() + "";String inSign = null;try {inSign = ShaUtils.SINGLETON.sha(inRawSign);} catch (Exception e) {throw new IllegalArgumentException("验证参数签名失败!");}if (!inSign.equals(in.getSign())) {throw new IllegalArgumentException("验证参数签名失败!");}String decryptedData = null;try {decryptedData = RsaUtils.decryptByPrivateKey(RsaProperties.privateKey, in.getData());} catch (Exception e) {throw new IllegalArgumentException("参数解密失败:" + in.getData(), e);}ByteArrayInputStream inputStream = new ByteArrayInputStream(URLDecoder.decode(decryptedData, "UTF-8").getBytes(Charset.forName("UTF-8")));return new MappingJacksonInputMessage(inputStream, inputMessage.getHeaders());}
}

响应解析器

package com.wx.security;import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJacksonValue;
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.JsonViewResponseBodyAdvice;import java.net.URLEncoder;/****/
@ControllerAdvice
public class CustomResponseBodyAdvice extends JsonViewResponseBodyAdvice {private final ObjectMapper objectMapper;public CustomResponseBodyAdvice(ObjectMapper objectMapper) {this.objectMapper = objectMapper;}@Overridepublic boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {return RsaProperties.enSwitch;}@Overrideprotected void beforeBodyWriteInternal(MappingJacksonValue bodyContainer, MediaType contentType,MethodParameter returnType, ServerHttpRequest request,ServerHttpResponse response) {if (request.getMethod() == HttpMethod.POST) {EncryptModel out = new EncryptModel();out.setTimestamp(System.currentTimeMillis());String decryptedData = null;try {final String jsonStr = objectMapper.writeValueAsString(bodyContainer.getValue());decryptedData = RsaUtils.encryptByPublicKey(RsaProperties.publicKey, URLEncoder.encode(jsonStr, "UTF-8"));out.setData(decryptedData);} catch (Exception e) {throw new IllegalArgumentException("返回对象加密失败:" + bodyContainer.toString(), e);}String rawSign = out.getTimestamp() + "";try {out.setSign(ShaUtils.SINGLETON.sha(rawSign));} catch (Exception e) {throw new IllegalArgumentException("返回对象签名失败:" + rawSign);}bodyContainer.setValue(out);}}
}

加密后接收对象

package com.wx.security;/*** @Author: 遛猫达人* @Description: TODO* @DateTime: 2021/8/9 11:44**/
public class EncryptModel {private String data;private Long timestamp;private String sign;public String getData() {return data;}public void setData(String data) {this.data = data;}public Long getTimestamp() {return timestamp;}public void setTimestamp(Long timestamp) {this.timestamp = timestamp;}public String getSign() {return sign;}public void setSign(String sign) {this.sign = sign;}
}

签名工具:

package com.wx.security;import org.apache.commons.codec.binary.Hex;import java.security.MessageDigest;public enum ShaUtils {/*** SINGLETON*/SINGLETON;private static final String SECRET = "xxx";private static final String CHARSET = "UTF-8";/*** @param raw* @return* @throws Exception*/public String sha(String raw) throws Exception {MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");messageDigest.update((SECRET + raw).getBytes(CHARSET));return Hex.encodeHexString(messageDigest.digest());}}

前端加密

  post(api, data, options) {var newData = {};if(store.state.enSwitch){let jsonData = JSON.stringify(data);// console.log(jsonData)newData.timestamp = new Date().getTime();let shaContent = sha_prefix + newData.timestamp;newData.sign =sha256(shaContent);newData.data = encrypt(encodeURIComponent(jsonData));}else{newData = data}return this.getRequest(api, newData, options, 'POST') },

前端解密

          //解密post请求响应if(store.state.enSwitch && method === 'POST'){let sign =sha256(sha_prefix + res.data.timestamp);if(sign !== res.data.sign){reject(res)uni.showToast({icon: 'none',title: "签名校验不通过"})return false}let resData = decrypt(res.data.data);// console.log(api+":"+decodeURIComponent(resData))res.data = JSON.parse(decodeURIComponent(resData))}

注意:使用前端加解密工具前先要执行以下命令安装相关依赖包:
npm install jsencrypt
npm install js-sha256
npm i encryptlong -S
前端加解密工具:

import JSEncrypt from 'encryptlong'const publicKey = 'xxx'const privateKey = 'yyyy'// 加密
export function encrypt(txt) {const encryptor = new JSEncrypt()encryptor.setPublicKey(publicKey) // 设置公钥,注意此处不要再设置私钥return encryptor.encryptLong(txt) // 对需要加密的数据进行加密
}// 解密
export function decrypt(txt) {const encryptor = new JSEncrypt()encryptor.setPrivateKey(privateKey)// 设置私钥,注意此处不要再设置公钥return encryptor.decryptLong(txt)
}

后端加解密工具:

package com.wx.security;import org.apache.commons.codec.binary.Base64;import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;/*** @author* @description Rsa 工具类,公钥私钥生成,加解密* @date**/
public class RsaUtils {private static final String SRC = "1123456";public static final String RSA = "RSA";/** *//*** RSA最大加密明文大小*/private static final int MAX_ENCRYPT_BLOCK = 117;/** *//*** RSA最大解密密文大小*/private static final int MAX_DECRYPT_BLOCK = 128;public static final String CHARSET_NAME = "UTF-8";/*** @param args* @throws Exception*/public static void main(String[] args) throws Exception {System.out.println("\n");RsaKeyPair keyPair = generateKeyPair();System.out.println("公钥:" + keyPair.getPublicKey());System.out.println("私钥:" + keyPair.getPrivateKey());System.out.println("\n");test1(keyPair);System.out.println("\n");}/*** 公钥加密私钥解密*/private static void test1(RsaKeyPair keyPair) throws Exception {System.out.println("***************** 公钥加密私钥解密开始 *****************");String text1 = encryptByPublicKey(keyPair.getPublicKey(), RsaUtils.SRC);String text2 = decryptByPrivateKey(keyPair.getPrivateKey(), text1);System.out.println("加密前:" + RsaUtils.SRC);System.out.println("加密后:" + text1);System.out.println("解密后:" + text2);if (RsaUtils.SRC.equals(text2)) {System.out.println("解密字符串和原始字符串一致,解密成功");} else {System.out.println("解密字符串和原始字符串不一致,解密失败");}System.out.println("***************** 公钥加密私钥解密结束 *****************");}/*** 私钥解密** @param privateKeyText 私钥* @param text           待解密的文本* @return /* @throws Exception /*/public static String decryptByPrivateKey(String privateKeyText, String text) throws Exception {byte[] keyBytes = Base64.decodeBase64(privateKeyText);PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);KeyFactory keyFactory = KeyFactory.getInstance(RSA);Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());cipher.init(Cipher.DECRYPT_MODE, privateK);byte[] encryptedData = Base64.decodeBase64(text);int inputLen = encryptedData.length;ByteArrayOutputStream out = new ByteArrayOutputStream();int offSet = 0;byte[] cache;int i = 0;// 对数据分段解密while (inputLen - offSet > 0) {if (inputLen - offSet > MAX_DECRYPT_BLOCK) {cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);} else {cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);}out.write(cache, 0, cache.length);i++;offSet = i * MAX_DECRYPT_BLOCK;}byte[] decryptedData = out.toByteArray();out.close();return new String(decryptedData, CHARSET_NAME);}/*** <p>* 公钥加密* </p>** @param str       源数据* @param publicKey 公钥(BASE64编码)* @return* @throws Exception*/public static String encryptByPublicKey(String publicKey, String str)throws Exception {byte[] data = str.getBytes(CHARSET_NAME);byte[] keyBytes = Base64.decodeBase64(publicKey);X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);KeyFactory keyFactory = KeyFactory.getInstance(RSA);Key publicK = keyFactory.generatePublic(x509KeySpec);// 对数据加密Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());cipher.init(Cipher.ENCRYPT_MODE, publicK);int inputLen = data.length;ByteArrayOutputStream out = new ByteArrayOutputStream();int offSet = 0;byte[] cache;int i = 0;// 对数据分段加密while (inputLen - offSet > 0) {if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);} else {cache = cipher.doFinal(data, offSet, inputLen - offSet);}out.write(cache, 0, cache.length);i++;offSet = i * MAX_ENCRYPT_BLOCK;}byte[] encryptedData = out.toByteArray();out.close();return Base64.encodeBase64String(encryptedData);}/*** 构建RSA密钥对** @return /* @throws NoSuchAlgorithmException /*/public static RsaKeyPair generateKeyPair() throws NoSuchAlgorithmException {KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(RSA);keyPairGenerator.initialize(1024);KeyPair keyPair = keyPairGenerator.generateKeyPair();RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();String publicKeyString = Base64.encodeBase64String(rsaPublicKey.getEncoded());String privateKeyString = Base64.encodeBase64String(rsaPrivateKey.getEncoded());return new RsaKeyPair(publicKeyString, privateKeyString);}/*** RSA密钥对对象*/public static class RsaKeyPair {private final String publicKey;private final String privateKey;public RsaKeyPair(String publicKey, String privateKey) {this.publicKey = publicKey;this.privateKey = privateKey;}public String getPublicKey() {return publicKey;}public String getPrivateKey() {return privateKey;}}
}

碰到的问题:

  • rsa加密字符串的长度最大为117个字节,所以前后端都必须使用分段加密和分段解密
  • 如果加密内容有中文,一定要先用js的encodeURIComponent方法或者java的URLEncoder.encode方法编码后再加密,然后用js的decodeURIComponent方法或者java的URLDecoder.decode方法解密,否则直接对中文加解密时偶尔会失败。
  • 公钥和私钥对的生成一定要小心,我刚开始使用命令在linux上生成的公钥和私钥,但是前端能加密,后端不能解密,后来发现生成的密钥对类型有问题,后端代码无法用它来解密,然后改成了用后端代码来生成密钥对。
  • 前端加解密方法中官方网站推荐把公钥和私钥都设置到JSEncrypt对象中,再进行加解密,说这样可以提高加解密的概率。但是我这样设置了之后java后台代码无法解密,然后改成了前端加密就只设置公钥,前端解密就只设置私钥就正常了。

对app请求的参数和响应进行rsa加密和解密相关推荐

  1. 返回结果乱码_Spring请求参数和响应结果全局加密和解密(1)

    阅读文本大概需要25分钟. 前提 前段时间在做一个对外的网关项目,涉及到加密和解密模块,这里详细分析解决方案和适用的场景.为了模拟真实的交互场景,先定制一下整个交互流程.第三方传输(包括请求和响应)数 ...

  2. springmvc 全局编码_SpringMVC请求参数和响应结果全局加密和解密

    前提 前段时间在做一个对外的网关项目,涉及到加密和解密模块,这里详细分析解决方案和适用的场景.为了模拟真实的交互场景,先定制一下整个交互流程.第三方传输(包括请求和响应)数据报文包括三个部分: 1.t ...

  3. Spring请求参数和响应结果全局加密和解密(1)

    阅读文本大概需要25分钟. 前提 前段时间在做一个对外的网关项目,涉及到加密和解密模块,这里详细分析解决方案和适用的场景.为了模拟真实的交互场景,先定制一下整个交互流程.第三方传输(包括请求和响应)数 ...

  4. java rsa加密解密_前端实现对请求参数进行RSA加密amp;解密,针对字符串过长进行分段加密amp;分段解密的处理...

    前言 在需求开发中,为了安全起见,我们都会难免遇到需要对一些敏感参数进行加密或者解密.所以,今天给大家分享的就是使用jsencrypt对请求参数进行RSA加密与解密,发这篇文章其实主要因为近期我的一位 ...

  5. 唯品会app请求头参数authorization的逆向分析与算法还原

    声明:本文内容仅供学习交流,严禁用于商业用途,否则由此产生的一切后果均与作者无关.如有冒犯,请联系我删除. 一.说明 app版本: v7.45.6 下载地址:aHR0cHM6Ly93d3cud2FuZ ...

  6. App 请求校验/加密方式总结

    为什么80%的码农都做不了架构师?>>>    最近对几款App(橙光, 闪艺),微信小程序(链家,汽车之家,)进行破解,学习了请求的 加签.验签/加密.解密方式,特做总结. 常见的 ...

  7. IW会话参数、请求信息、及其响应信息

    目录 IW会话参数.请求信息.及其响应信息 一.IW新会话参数 1.MS Edge浏览器: 2.Delphi FMX APP: 二.IW请求信息-App客户端 三.IW请求信息及其响应信息-App客户 ...

  8. 存储http请求返回参数_前端学习需要知道的 HTTP 知识(1/7)

    基础知识 场景 我们打开浏览器,输入网址(比如 https://www.bilibili.com/),然后我们就可以看到 b 站的 Web 页面,Web 页面当然不能凭空显示出来.根据 Web 浏览器 ...

  9. HTTP请求报文和HTTP响应报文(转)

    原文地址:http://blog.csdn.net/zhangliang_571/article/details/23508953 HTTP报文是面向文本的,报文中的每一个字段都是一些ASCII码串, ...

最新文章

  1. java批处理框架采集端_使用Spring Batch批处理框架(参考)
  2. 大数据可视化模板_最佳大数据可视化技术
  3. c++ 四舍五入保留两位小数_Excel中保留小数点位数
  4. 我去,还在这样读写 excel 这也太低效了吧,好办法来了
  5. cv2.cornerHarris()详解 python+OpenCV 中的 Harris 角点检测
  6. 【BZOJ3226】【codevs2297】校门外的区间,线段树
  7. cocos creator 安卓原生平台环境_cocos creator原生平台下载文件并保存到本地
  8. [转载] numpy.inf
  9. python中怎么定义二维数组_如何在Python中定义二维数组
  10. H5网页去除苹果手机底部白边
  11. 自动驾驶 Automotive SPICE(ISO/IEC 15504) 和CMMI有什么不同?
  12. MATLAB中round函数的使用
  13. 前端xlsx插件简单说明
  14. PHP时间戳与日期的相互转换
  15. 小米红米1S 电信/联通版 专用TWRP2.8.1.1中文版 (全屏触摸/支持MTP挂载内外置存储)...
  16. 调用腾讯地图 输入地址获取经纬度
  17. HTTP请求偶尔失败(21秒后超时) - 问题排查
  18. 绘制3D海水温盐密度曲面(matplotlib)
  19. 计算机选北航还是南科大,今年高考招生“卷中卷”,大一变高四,北航四大试验班,南科大……...
  20. windows安装rabbitmq详细实例

热门文章

  1. “360安全卫士优化后,输入法图标丢失”解决方法
  2. 近世代数 笔记和题型连载:第一章(代数系统引入)
  3. 你好,三角形(LearnOpenGL With Qt)
  4. Nginx尚硅谷学习笔记
  5. BIM在国内建筑全生命周期的20个典型应用
  6. 微信菜单 html页面添加的,微信公众号添加菜单栏外部链接(微信添加外部链接方法)...
  7. arcgis10.0及以上版本,使用arcpy加载在arctoolbox中批量添加同一图层
  8. 如何在5天内学会Vue?聊聊我的学习方法!
  9. 分享一些我的学习方法
  10. 对于语句:f=(3.0,4.0,5.0),(2.0,1.0,0.0);的判断中,( )是正确的。