news 2026/7/18 23:51:32

纯JS实现的加解密方法

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
纯JS实现的加解密方法

crypto-js@4.2.0 + uni-app 请求封装 + AES 加解密 + URL 编码

一、安装指定版本依赖

npm install crypto-js@4.2.0 --save

安装后查看package-lock.json确认片段:

"crypto-js": { "version": "4.2.0", "resolved": "https://registry.npmmirror.com/crypto-js/-/crypto-js-4.2.0.tgz", "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==" }

二、新建工具文件@/utils/aesDecrypt.js

包含:解密函数、配套加密函数passwordEncryption(请求里用到的加密方法)、统一封装解密后端数据

import CryptoJS from 'crypto-js'; /** * AES解密方法 (CBC模式) * @param {String} encryptedData 加密后的Base64密文 * @param {String} key 密钥(16位AES-128) * @param {String} iv 初始化向量 固定16位 * @returns {String} 解密后原始字符串 */ export function aesDecrypt(encryptedData, key, iv) { try { // Base64密文转WordArray const encryptedHexStr = CryptoJS.enc.Base64.parse(encryptedData); // 密钥、iv转WordArray const keyHex = CryptoJS.enc.Utf8.parse(key); const ivHex = CryptoJS.enc.Utf8.parse(iv); const decrypted = CryptoJS.AES.decrypt( { ciphertext: encryptedHexStr }, keyHex, { iv: ivHex, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 } ); return decrypted.toString(CryptoJS.enc.Utf8); } catch (error) { console.error('AES解密失败:', error); throw new Error('解密失败,请检查密钥、向量和密文是否正确'); } } /** * AES加密方法(对应前端请求参数加密 passwordEncryption) * @param {String} plainText 原始明文JSON字符串 * @param {String} key 密钥 * @param {String} iv 向量 * @returns {String} Base64密文 */ export function passwordEncryption(plainText, key, iv) { const keyHex = CryptoJS.enc.Utf8.parse(key); const ivHex = CryptoJS.enc.Utf8.parse(iv); const encrypted = CryptoJS.AES.encrypt(plainText, keyHex, { iv: ivHex, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }); return encrypted.toString(); } /** * 统一解密后端返回加密数据 * 内置约定好的固定key、iv,与后端保持一致 * @param {String} encryptedData 后端返回Base64密文 * @returns {String} 原始JSON字符串 */ export function decryptBackendData(encryptedData) { // 业务固定密钥IV,正式环境建议接口下发不硬编码 const key = '1111111111111111'; // 修正:必须严格16位 const iv = '1111111111111111'; // 修正:必须严格16位 return aesDecrypt(encryptedData, key, iv); } /** * 统一加密请求参数(内置固定key iv) * @param {String} plainText JSON字符串 * @returns {String} Base64密文 */ export function encryptReqData(plainText) { const key = '1111111111111111'; const iv = '1111111111111111'; return passwordEncryption(plainText, key, iv); }

三、request 请求封装增加

import config from '../static/config.js'; // 引入加解密工具 import { decryptBackendData, encryptReqData } from '@/utils/aesDecrypt.js'; // 你的全局配置、接口域名、config等自行保留 import config from '@/config/index.js'; const fetch = { interfaceUrl: function() { //接口地址 let server =config.server(); return server; }, }; export default { /** * 通用请求封装 * @param {String} url 接口地址 * @param {Object} postData 请求参数 * @param {String} method 请求方式 GET/POST * @param {Boolean} type true:x-www-form / false:json * @param {Boolean} showLoading 是否展示loading * @returns {Promise} */ request: function(url, postData, method, type, showLoading) { // 展示加载弹窗 if (showLoading) { uni.showLoading({ mask: true, title: '请稍候...' }) } // 自动注入openid if (postData) { const userStorage = uni.getStorageSync('user'); if (userStorage && userStorage.length) { postData.openid = userStorage; } } console.log('当前请求地址', url); console.log('原始请求参数', postData); //从这开始 // 接口白名单:白名单接口不加密请求参数 const encryptWhiteList = [ '/rsp/api/rsp/inspector/snapshot/save', '/rsp/api/rsp/inspector/create/lunei/order/time' ]; // 不在白名单则加密参数 if (!encryptWhiteList.includes(url)) { // 1. 对象转JSON字符串 2.AES加密 3.URL编码防止+ / = 被转义 const jsonStr = JSON.stringify(postData); const aesCipher = encryptReqData(jsonStr); postData = { encryptData: encodeURIComponent(aesCipher) }; console.log('加密后请求参数 postData', postData); } return new Promise((resolve, reject) => { uni.request({ url: fetch.interfaceUrl() + url, data: postData, header: { 'content-type': type ? 'application/x-www-form-urlencoded' : 'application/json', 'authorization': uni.getStorageSync("token") || "", 'security': 1, 'merchantid': config.merchantid(), 'code': uni.getStorageSync("code") || "" }, method: method, dataType: 'json', timeout: 60000, success: (res) => { // 返回数据解密白名单,该接口不解密后端数据 const decryptWhiteList = [ '/rsp/api/rsp/user/order/jieshunadd' ]; if (!decryptWhiteList.includes(url)) { try { const jsonData = res.data; // 解密Base64密文 const decodeData = decryptBackendData(jsonData); // 覆盖res为解密后的真实JSON对象 res = JSON.parse(decodeData); } catch (err) { console.error('返回数据解密解析失败', err); } } console.log('当前返回地址', url); console.log('处理后返回数据', res); showLoading && uni.hideLoading(); resolve(res); }, fail: (res) => { uni.showToast({ title: res.errMsg || "出错啦~", icon: 'none', duration: 2000 }); // 失败回调尝试解密返回数据 if (url !== '/' && url !== '') { const jsonData = res.data; if (jsonData) { try { const decodeData = decryptBackendData(jsonData); res = JSON.parse(decodeData); } catch (err) { console.error('失败回调解密异常', err); } } } showLoading && uni.hideLoading(); reject(res); } }) }) } }

四、加密开关优化方案总结

1. 配置文件static/config.js新增加密控制开关

作用:一键全局开启 / 关闭接口 AES 加密,无需修改请求逻辑

// 接口加密开关:0=关闭加密,1=开启AES加密 const encrypt = function() { return 1; } module.exports = { encrypt: encrypt }

2. 请求文件request.js接入开关控制

  1. 引入配置:import config from '../static/config.js'
  2. 在原有非白名单加密判断处增加开关校验:
// 原判断:不在白名单就加密 // 修改后:不在白名单 且 加密开关=1 才执行加密 if (!encryptWhiteList.includes(url) && config.encrypt() === 1) { // AES加密 + encodeURIComponent编码逻辑不变 }
  1. 调试 / 本地环境将 config 中 return 改为 0,全局接口明文传输;线上改为 1 开启加密。

五、注意小问题

1.encodeURIComponent 在 AES 加密场景

(1). 两个 URL 编码函数区分

方法转义范围使用场景
encodeURI仅转义 URL 非法字符,保留&=/?:拼接完整页面 URL,不用于接口参数
encodeURIComponent转义+ / = & ? 空格等所有特殊符号接口传输 AES 密文必须使用

(2). AES 密文必须编码的核心原因

CryptoJS 加密输出标准 Base64 字符串,包含三类危险字符:+ / =

  • URL 传输时+会被后端自动解析为空格
  • /=会切割参数键值对,密文结构损坏

没有这个,最终结果:导致后端解密失败、返回空字符串(这个问题要注意)

2. 前后端配套流程

  1. 前端请求加密流程对象 JSON 字符串 → AES 加密得到 Base64 密文 →encodeURIComponent(密文)→ 传给后端
  2. 后端接收解密流程接收参数 →decodeURIComponent(加密字符串)还原原始 Base64 → AES 解密
  3. 解密时必须先执行decodeURIComponent,再传入 AES 解密函数,顺序不可颠倒

3.GET 请求

GET 请求 URL 拼接参数强制使用 encodeURIComponent

例子:

encodeURIComponent this.$request(/rsp/api/stc/park/?keyapp=berthcodeupdateplateno, 'GET').then((res) => { if (res.data.keyappvalue == '1') { this.plateNoBtn = true } this.getDetail (); })

你封装的$request内部会自动处理参数拼接 + 编码,这种自动传keyapp过去的,后端收不到的,要修改成以下的,就可以实现postdata加密传输过去

this.$request( this.$request(/rsp/api/stc/park/, {"keyapp": "berthcodeupdateplateno"}).then((res) => {.then((res) => { if (res.data.keyappvalue === '1') { this.plateNoBtn = true } this.getDetail() })
  • 请求封装内部会自动对每个参数执行encodeURIComponent编码,适配 AES 密文、特殊字符场景;
  • 参数和 url 分离,后续新增参数直接往对象里加,不用手动拼接&,可读性、维护性更强;
  • 统一走你之前配置的加密开关逻辑,若接口不在白名单会自动加密。

3. POST-JSON 传参

axios 会自动处理转义,但统一编码能兼容所有后端框架,建议保留

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/18 23:49:01

瑞德克斯的资料流程会更安心吗?

看瑞德克斯时,很多人会先关心账户安全、资料流程和规则边界是否讲得细。从规则边界角度观察,平台把复杂事项拆解得更容易理解,使用者自然更容易形成稳定印象。因此,文章如果从场景、说明和服务边界展开,会比空泛称赞更…

作者头像 李华
网站建设 2026/7/18 23:47:11

多 Agent 协作——子代理、团队与任务编排

Task 系统 -- 用任务追踪多步骤工作的进度和结果Team 消息传递 -- 多个 Agent 组成团队,通过邮箱系统互相通信这篇文章逐一分析这三个层面的实现,最后看它们怎么组合起来做任务编排。一、子 Agent:SubAgentSpawner 协议与 AgentToolSubAgent…

作者头像 李华
网站建设 2026/7/18 23:44:36

【干货】会计专业二本就业:别再只盯着事务所了,2026年这条路更现实

最近很多二本会计同学投简历投到怀疑人生,尤其是看到岗位写着本科以上、财会相关、熟练Excel,结果一问薪资才4K-6K。我也是小镇做题家一路卷过来的,身边会计、审计、财务、数据岗的朋友不少,坑也见过不少。今天就用学长视角讲讲会…

作者头像 李华
网站建设 2026/7/18 23:41:52

成立一年营收破千万,大衍科技如何卡位具身智能数据缺口?

摘要:合成数据一帧几毛钱、毛利率超60%来源:朝阳资本论作者:乌子柒2026年7月,空间智能公司大衍科技宣布完成数千万元天使轮融资,由松禾资本领投,浙江省省金控与广州番禺创新基金等国资背景机构参与。这家成…

作者头像 李华
网站建设 2026/7/18 23:39:24

Trae 企业项目实战:从需求拆解、原型到全栈开发与部署

本文不讨论“输入一句话,AI 自动做完一个系统”。企业项目真正难的,是需求边界、数据模型、权限、测试和交付。Trae 能缩短编码和排错时间,但前提是把这些约束写清楚,并让每次变更都能检查、回退和验收。 引言 Trae IDE 有 IDE 和…

作者头像 李华