做前端时间久了,一定会遇到一个绕不开的话题:登录密码要不要在提交前加密?接口参数能不能防止被篡改?文件下载要不要校验完整性?这些问题背后,其实都是同一个知识栈——AES对称加密、RSA非对称加密、MD5哈希算法。很多同学一听到这三个名词就觉得头皮发麻,觉得这是后端或者安全工程师的活儿,跟日常写页面没什么关系。但实际上,只要你接手过登录接口、支付回调、数据上报,或者任何一个稍微正经一点的业务系统,你都会在某个版本迭代里被突袭式地问一句:“这个字段为什么是明文?”
这篇文章会把三个算法的原理、前端落地姿势、代码示例和踩坑实录一次性讲清楚,尽量用大白话解释,把每次加密选型背后的“为什么”也拆开。看完以后你至少能做到:知道什么场景该用AES、什么场景该用RSA、什么场景MD5只是一个辅助工具,并且能在浏览器和Node环境里亲手把代码跑起来。适合刚接触前端加密的新手,也适合被后端抛来一段加密SDK但完全看不懂的资深页面仔。
1. 三种算法的角色定位:先搞清楚谁在干什么
1.1 对称加密与非对称加密的关系,像保险柜和钥匙匣
AES属于对称加密,特点是加密和解密用同一把密钥。你可以把AES想象成一把能锁能开的普通挂锁:锁门和开门用的是同一把钥匙。它的优势非常明显——速度快、硬件支持好、加密大数据量时开销小。拿真实数据说话,一台普通电脑上跑AES-256-GCM,每秒加密几百MB的数据量毫无压力,这速度是RSA完全追不上的。
RSA属于非对称加密,特点是加密解密的钥匙不一样,分为公钥和私钥。公钥随便发给别人,负责加密;私钥只留在自己手里,负责解密。这里我用一个更贴切的类比:公钥是任何人都可以往里投信的邮箱口,私钥是只有你能打开的那个邮箱锁。别人把信塞进去,哪怕经过很多双手,也没人能在不砸邮箱的情况下把信拿出来。RSA的问题也一眼可见:算法复杂、速度很慢,加密几百字节的小数据还行,真要加密几MB的数据会跑得让人怀疑人生。
所以常规做法是把两者结合起来:用AES加密大数据,用RSA加密那个AES密钥。也就是前端随机生成一个AES密钥,然后用后端下发的RSA公钥把这个AES密钥加密,上传给后端;后端用RSA私钥解出AES密钥,再用这把AES密钥去解业务密文。这个模式叫混合加密,是HTTPS背后TLS握手的核心逻辑,也是很多自定义安全协议的标配。理解了这一层,再看任何一套加密方案,你脑子里会有一个框架:底层业务数据用对称加密,密钥传递用非对称加密。
1.2 MD5是哈希算法,不是加密算法,这层窗户纸必须捅破
MD5和AES、RSA最大的区别在于:它不是可逆的。MD5把任意长度的数据映射成一个固定长度(128位)的十六进制字符串,理论上你无法从这串字符串反推出原始数据。所以严格来说,MD5不是加密,加密至少要求能解密,哈希是单向的、不可逆的。它是为了“校验完整性”和“生成指纹”而生的。
日常开发里最常见的误解就是“密码用MD5加密一下存数据库”。这句话在安全的视角下等于没说,因为MD5只做散列,不加盐、不迭代、无密钥,根本扛不住彩虹表和碰撞攻击。正确的密码存储姿势是bcrypt、scrypt、argon2这类专用密码哈希算法,它们故意把计算过程设计得很慢,让暴力破解的成本成倍上升。所以MD5在前端更适合干这些事:文件校验、缓存key、短链指纹、防篡改签名。它承担的是“证明数据没被改过”的职责,而不是“让数据不被人看见”的职责。
想明白这个角色区别,你就能避免很多低级设计失误。加密解决的是机密性问题,哈希解决的是完整性问题,签名解决的是真实性问题。三者协同工作才是完整的安全链路,光靠某一个算法扛不起所有需求。
2. AES对称加密在前端的落地实操
2.1 算法模式怎么选:别碰ECB,优先GCM
AES本身是个原始算法,使用时有多种工作模式,常见的有ECB、CBC、GCM、CCM。ECB模式有一个非常经典的问题:相同的明文块会被加密成完全相同的密文块,从密文上能直接看出明文的规律性,比如图片加密后还能看到轮廓,这等于没加密。而CBC模式虽然引入了IV(初始向量),解决了ECB的规律性问题,但它不提供完整性校验——密文被篡改后解密端根本发现不了,需要额外叠加MAC或者HMAC才能保证不被篡改。
GCM模式是目前最推荐的选择。它天然内置了认证标签(authTag),不仅加密,还能校验数据有没有被篡改。也就是说,GCM同时解决机密性和完整性两个问题,一石二鸟。在前端环境和后端服务对接时,只要后端支持GCM,优先选它准没错。密钥长度方面,128位在理论上已经具备足够安全性,但工程上大家更习惯直接用256位,多出的那一点点性能开销几乎可以忽略,却可以名正言顺地在验收报告里写上AES-256。这里有个细节:GCM加密的IV推荐12字节(96位),这是一个和底层实现高度相关的约定,前后端必须完全一致,否则解不开。
2.2 浏览器端用Web Crypto API跑通AES-GCM
现代浏览器都原生支持Web Crypto API,不需要引入额外的加密库。用它做AES-GCM加解密,比我以前用第三方库时要干净得多,也更难踩到库版本差异的坑。下面是浏览器端加密一段文本的完整示例:
// 生成一个256位的AES密钥 const key = await crypto.subtle.generateKey( { name: 'AES-GCM', length: 256, }, true, ['encrypt', 'decrypt'] ); // 加密函数:输入原文,输出iv + 密文 + authTag的Base64组合 async function encryptWithAesGcm(plainText, aesKey) { const encoder = new TextEncoder(); const data = encoder.encode(plainText); const iv = crypto.getRandomValues(new Uint8Array(12)); // 12字节随机IV const encrypted = await crypto.subtle.encrypt( { name: 'AES-GCM', iv: iv, }, aesKey, data ); // encrypted 是 ArrayBuffer,密文后面自动跟着16字节的authTag const ciphertext = new Uint8Array(encrypted); const combined = new Uint8Array(iv.length + ciphertext.length); combined.set(iv, 0); combined.set(ciphertext, iv.length); return bufferToBase64(combined); } function bufferToBase64(buffer) { const bytes = new Uint8Array(buffer); let binary = ''; bytes.forEach((b) => (binary += String.fromCharCode(b))); return window.btoa(binary); }这个代码有三个关键点需要重点说。第一,IV每次加密都必须重新生成,绝对不能固定。如果IV固定,相同明文会产生相同密文,等于泄露了明文规律,GCM的安全性基础就崩塌了。第二,crypto.subtle.encrypt返回的密文末尾已经自动附带了16字节的GCM认证标签,前端不需要单独处理标签,直接把iv和密文拼在一起发给后端即可。第三,加密后的数据是二进制格式,不能直接放进JSON里传输,必须先转成Base64或十六进制字符串,我这里选择了Base64,传输体积增加约33%。
解密端同样用Web Crypto API完成,逻辑是反过来的:从Base64里拆出前12字节作为IV,剩下的部分作为密文加认证标签,然后调用crypto.subtle.decrypt。如果通信过程中有任何一位数据被篡改,decrypt会直接抛出一个OperationError,这就是GCM自带的完整性校验在起作用。这个报错是好事,说明设计真的在拦截恶意修改。
2.3 Node.js侧如何解密前端传来的AES密文
如果前端工作在浏览器,而后端是Node.js服务,经常会遇到两边加密库不同导致的对不齐问题。Node端用内置的crypto模块就能完成解密,不需要额外安装任何包。关键点在于:前端把iv和密文拼在一起发过来,后端必须按同样的顺序拆分,并且使用同样的加密算法、同样的密钥、同样的字节长度。
const crypto = require('crypto'); function decryptAesGcm(combinedBase64, keyBuffer) { const combined = Buffer.from(combinedBase64, 'base64'); const iv = combined.subarray(0, 12); const authTag = combined.subarray(combined.length - 16); const ciphertext = combined.subarray(12, combined.length - 16); const decipher = crypto.createDecipheriv('aes-256-gcm', keyBuffer, iv); decipher.setAuthTag(authTag); let decrypted = decipher.update(ciphertext, null, 'utf8'); decrypted += decipher.final('utf8'); return decrypted; }这个示例里我单独拆出了authTag,原因是Node的createDecipheriv不会像浏览器那样自动识别附加在密文尾部的标签,需要手工切出来并通过setAuthTag喂给它。前端可能会觉得浏览器端计算比较方便,但为了跨端一致,建议团队内固定下来同一种报文组织方式,比如统一规定:12字节IV + 密文 + 16字节authTag,Base64编码后传输。这个约定如果前后端各写各的,后面联调时一定会踩到“解密出来是乱码”的坑。
2.4 AES实操里的四个常见坑
第一个坑是密钥格式。前端生成的AES密钥默认是CryptoKey对象,不能直接当成字符串发给后端,需要先用crypto.subtle.exportKey('raw', key)导出成ArrayBuffer,再转成Base64传给后端。反过来,后端传过来的密钥字符串,前端要通过crypto.subtle.importKey('raw', base64ToBuffer, { name: 'AES-GCM' }, true, ['encrypt'])导成CryptoKey才能使用。这两个方向搞反任何一个,都会在importKey环节就报DataError。
第二个坑是编码不一致。前端把密文转成了Base64,后端却按hex去解析,结果自然是乱码;前端拼数据时用了Uint8Array组合,后端在拆数据时却用Buffer.concat拼接,两者底层字节顺序也可能不一致。解决方案没有捷径,只能靠严格约定通信格式,并且把格式检查写进接口文档。
第三个坑是IV复用。有人为了图方便,把IV写死成固定值,这在GCM模式里属于严重的安全事故。GCM的安全性依赖每次加密使用不同随机IV,一旦IV复用,同一密钥下不同密文之间的内部关系就可能被破译者利用,等于白加密了。所以务必确保每次调用加密函数都重新生成IV。
第四个坑是“密钥写在前端代码里”。说实话,前端代码里的任何静态密钥都等于公开密钥,用户打开开发者工具就能看到。AES密钥必须由后端动态下发,或者在每次会话中随机生成并通过RSA信封传递,绝不能写死在config.js里。遇到这种需求,我一般直接回怼需求方:这不是加密,这是自欺欺人。
3. RSA非对称加密的正确姿势
3.1 密钥长度和填充方式:这两个参数别乱定
RSA的安全级别建立在密钥长度和大数分解的难度之上。目前业内共识是至少2048位,一些安全性要求更高的系统会直接上3072位或4096位。长度增加会直接影响加密、解密速度和密钥体积,但前端通常只做公钥加密,性能压力没那么大,所以建议默认2048位起步,有钱有性能再考虑更长的。
填充方式也是在联调时最容易翻车的地方。常见有PKCS#1 v1.5和OAEP两种。我在自己的项目里一律推荐RSA-OAEP,它在Web Crypto API中的名称是RSA-OAEP,配合SHA-256摘要算法使用,比PKCS#1 v1.5更抗攻击。很多后端同学默认配置是PKCS#1 v1.5,如果前端这边用OAEP解,两边一碰必然报错。最好的做法是在接口文档里明确标注:前端使用RSA-OAEP,后端对应算法是RSA/ECB/OAEPWithSHA-256AndMGF1Padding。看到这种参数别慌,它们算的是同一件事,只是Java、OpenSSL、Web Crypto三种生态的叫法不一样。
还需要知道一个致命限制:RSA加密的数据长度有上限。以2048位密钥配合OAEP填充为例,一次最多能加密约190字节左右的明文(256字节减去2*64字节哈希和填充开销)。所以RSA天生不适合加密大字段,更不能直接加密整个请求体。这也是为什么必须走混合加密:RSA只负责加密那一把随机的AES密钥,AES负责加密真正的业务JSON。
3.2 前端用RSA公钥加密的完整流程
常见场景是后端生成密钥对,把公钥以Base64编码的字符串方式下发到前端,前端拿到公钥后再去做加密。公钥通常采用PKCS#8或者SPKI格式,前端不能直接用字符串调用encrypt,必须先解析成ArrayBuffer,再走importKey导入成CryptoKey。下面是一个可以直接用的封装函数:
async function importRsaPublicKey(base64PublicKey) { const binaryDer = base64ToBuffer(base64PublicKey); return crypto.subtle.importKey( 'spki', binaryDer, { name: 'RSA-OAEP', hash: 'SHA-256', }, true, ['encrypt'] ); } async function encryptWithRsa(publicKeyBase64, plainText) { const publicKey = await importRsaPublicKey(publicKeyBase64); const data = new TextEncoder().encode(plainText); const encrypted = await crypto.subtle.encrypt( { name: 'RSA-OAEP', }, publicKey, data ); return bufferToBase64(encrypted); }这段代码背后的坑主要集中在base64ToBuffer这个函数上。公钥Base64字符串往往带有-----BEGIN PUBLIC KEY-----这类PEM头,传进来之前需要把头和换行符剥掉,只保留中间那一段密文内容。如果后端下发的是十六进制字符串,前端就得换一种解析方式。我建议把公钥格式统一为不带PEM头的纯Base64,由后端做格式处理,前端只负责导入和加密,双方责任边界会清晰很多。
3.3 私钥为什么绝对不能出现在前端
提到RSA就必然会有人问:能不能前端用私钥签名,后端用公钥验签?实际上,把私钥放在前端,等同于把保险柜钥匙挂在保险柜门上。前端代码是运行在用户浏览器里的,任何静态资源都可能被调试、抓包、篡改,私钥一旦被提取,整个公钥体系就形同虚设。前端需要“签名”的场景,正确做法应该是把签名放到服务端完成,前端只负责提交待签名的数据;如果一定要在客户端做身份认证,优先考虑WebAuthn、硬件密钥或者平台自带的生物识别能力,而不是把私钥写进JS包。
我见过某些项目为了省事,直接在前端预置一个固定RSA私钥用来做请求签名,结果运营同学用抓包工具分分钟就把私钥还原了出来。这个教训是:非对称加密的安全性建立在私钥保密的前提下,一旦私钥公开,任何加密都只是表演。前端的RSA职责只保留在“用公钥加密数据”,解密和签名都留给后端。
3.4 混合加密的报文结构长什么样
前端随机生成一个256位AES密钥,用RSA公钥加密AES密钥本身,同时用AES-GCM加密请求JSON,最后把一个完整的报文发给后端。报文结构大致是:
{ "encryptedKey": "Base64(RSA加密后的AES密钥)", "encryptedData": "Base64(IV + AES密文 + authTag)" }后端收到后,先用RSA私钥解密encryptedKey拿到AES密钥,再用这个AES密钥和报文里的IV去解密encryptedData。这种混合方案既绕开了RSA加密长度限制,又避免了AES密钥在网络上明文传输,几乎是目前自定义加密协议的最优解。相比直接使用HTTPS,它能额外保护接口层的数据,比如在网关、日志系统里不会暴露原始字段;同时又能让系统在复杂中间件环境下多一层纵深防御。需要强调的是,前端加密不能替代HTTPS,它和HTTPS不是二选一的关系,而是在正确使用HTTPS的前提下做进一步增强。
4. MD5哈希算法:安全边界和实用技巧
4.1 MD5能用在哪些地方
先说结论:MD5不能用于密码存储,也没有密钥参与,所以它不是“加密”技术。但MD5在工程里依然有很多不可替代的用途,它的特点是快、短、固定长度,适合生成指纹。前端可以利用MD5做下载文件的完整性校验:拿到文件后计算MD5,和服务器下发的MD5比对,一致就说明文件没有被篡改或传输损坏。缓存体系也经常拿它做key,比如把一长串URL参数通过MD5生成一个短ID,避免缓存键过长。
另一个常见场景是接口防篡改签名。前端把请求参数按字典序排序,拼上双方约定好的盐值(secret),整体计算MD5,把这个值放在请求头里。后端收到请求后按同样的规则重新计算,比对两份MD5是否一致,不一致就直接拒绝。这个方案的优点是实现简单,不依赖复杂的密钥协商;缺点是MD5本身可以被碰撞攻击,所以它只适合中等安全等级的业务,不适合高价值交易场景。如果需要更强安全性的签名,可以把MD5换掉,改成HMAC-SHA256这种带密钥的哈希算法,抵抗碰撞的能力会更强。
4.2 浏览器和Node里分别怎么算MD5
浏览器端的Web Crypto API不提供MD5算法支持,原因很简单:MD5已经被认为不够安全,浏览器厂商不愿意再为它提供原生接口。所以在浏览器里要算MD5,通常借助第三方库,比如crypto-js。注意使用前要确认一下库的整体体积和依赖情况,如果项目本身很小,只为了算一个MD5引入整个crypto-js,可以考虑用纯JS实现或者改用非对称签名的方案。
const CryptoJS = require('crypto-js'); const params = { mobile: '13800138000', amount: '99.00', timestamp: '1710000000', }; const secret = '你的约定盐值'; const signStr = Object.keys(params) .sort() .map((key) => `${key}=${params[key]}`) .join('&') .concat('&key=', secret); const sign = CryptoJS.MD5(signStr).toString();Node.js环境则简单得多,内置的crypto模块直接支持MD5:
const crypto = require('crypto'); function md5(content) { return crypto.createHash('md5').update(content).digest('hex'); }这里有两个小细节值得注意。一是拼签名串时,团队内部必须约定好“是否包含值为空的参数”“数组参数如何序列化”“嵌套对象的key排序规则”,否则前后端各写各的排序,签名永远对不上。二是crypto.createHash的update方法可以链式调用,如果内容是流式读取,一定要确保所有字节都写进去了再调用digest,漏掉任何一段数据,MD5结果都是错的。
4.3 签名能防篡改,但防不了重放
MD5签名解决的问题是“数据有没有被改过”,但它不关心请求是不是原来的请求。攻击者完全可以截获一个合法请求,原封不动地重放多次,后端如果每次都校验通过,就会造成重复下单、重复扣款之类的严重后果。这是我在实际项目中踩过的坑:只做了签名校验,没做防重放,结果线上出现了一笔订单被重复提交的案例。
标准解法是加上时间戳和随机数nonce。前端在参数里带上当前客户端时间戳和一次性随机字符串,后端校验签名通过后,再检查时间戳是否在允许的时间窗口内(比如±5分钟),并且检查这个nonce是否在有效期内的缓存中出现过。如果nonce已经出现过,说明这是一次重放请求,直接拒绝。这样设计后,重放窗口被压缩到几秒钟以内,风险就基本可控了。需要提醒的是,时间戳窗口和nonce缓存这些逻辑必须由后端来完成,前端只是被动带上这两个参数,不要指望前端做拦截,因为攻击者完全可以绕过前端。
5. 前端加密工程的选型和取舍思路
5.1 先想清楚加密目标,再选算法
我刚工作那会儿,遇到需求就条件反射地堆技术:参数加密、返回值加密、头部签名,恨不得把整个请求包装成密文。后来发现,这种过度设计不仅性能差,还给前后端联调制造了大量麻烦。加密方案的设计应该从威胁模型出发:系统到底要防谁?是防外部的抓包和重放,还是防内部的日志泄露,还是防用户篡改客户端数据?不同的威胁模型对应不同的方案,盲目堆料只会增加复杂度。
比如说,只是防第三方监听,那HTTPS已经足够,前端再加密一遍属于重复投入;如果系统里存在网关日志、审计平台会记录完整报文,那敏感字段级加密就有必要;如果担心用户直接修改请求参数刷接口,那签名字段就能解决问题;如果担心被重放攻击,那就组合时间戳和nonce。技术选型要多问几句“为什么”,而不是网上看到什么方案就抄什么方案。
5.2 密钥应该动态下发,而不是写死在包里
AES密钥写在前端是公开的,RSA公钥公开是没问题的,但私钥绝对不能出现。对于自定义加密协议,我强烈建议每次接入或每次会话都动态生成密钥。前端在会话开始时请求一个/api/security/public-key接口,后端返回RSA公钥;前端随机生成AES密钥,用公钥加密后作为握手请求的第一个字段发送;后端用私钥解出AES密钥,之后的整个会话都用这把AES密钥加密通信。这个流程可以和上面说的混合加密报文统一起来,每次会话密钥都是新的,即使某一次通信被破解,也不会影响后续会话的安全性,这种特性在安全领域叫前向保密。
实现起来并没有想象中那么复杂,无非是给现有前端请求层增加一个“获取公钥”和“加密请求体”的通用拦截器。但收益非常明显:不用再担心静态密钥泄露、密钥更新困难的问题。如果团队里没有专门的安全工程师,这个方案是我们能做出来的最有性价比的折中。
5.3 性能优化和包体体积控制
加密不是没有代价的。RSA加密2048位密钥数据时,耗时通常在几十毫秒到百毫秒之间,虽然一次请求加密很小,但如果是批量加密几百个字段,延迟会非常明显。AES-GCM的速度则快到几乎可以忽略,加解密几十KB数据也就是微秒级到毫秒级。因此在设计接口时,能不加密大数据就不加密大数据,能合并加密的字段就合并成一个JSON对象整体加密,减少调用次数。
还有一个容易被忽略的细节:压缩和加密的顺序。可压缩的数据必须先压缩再加密,因为加密后的数据近乎随机,压缩算法几乎找不到冗余信息,压缩率会急剧下降,等于白压。 HTTP传输层如果开启了HTTPS和可能的压缩协商,也要注意不要对已经加密的报文再做压缩,效率极低且浪费CPU。密文本身经过Base64编码后体积增加约三分之一,如果服务端和客户端都支持二进制传输,可以考虑直接用ArrayBuffer而不是Base64,能省一些流量。
5.4 前端加密不是银弹,别忽略全局风险
需要清醒认识到:前端加密的本质是提高了攻击门槛,而不是彻底阻断攻击。攻击者完全可以不破解你的加密协议,直接篡改前端代码,把加密函数替换成明文函数,或者干脆不发你的页面,自己构造请求。所以任何依赖前端来保证安全的设计,都必须同时具备后端的二次校验和风控逻辑。业务上的关键规则,比如价格、库存、优惠券状态,后端必须重新校验,不能轻信前端传来的任何值。前端加密保护的是传输过程和数据落盘后的泄露风险,而不是业务逻辑的正确性。
在落地前可以先画一张简单的数据流向图,把每个环节的敏感字段标出来:页面渲染时、请求发送时、网关记录时、服务端接收时、数据库存储时。哪一段是明文,哪一段是密文,一目了然。前端加密能管到的只是“请求发送时”和“部分存储前准备”这两段,其他环节的安全需要整个团队配合完成。这也是我一直强调的原因:加密是系统工程,不是写几个函数就完事。
6. 常见问题与排查技巧速查表
把这个表格贴在这里,是我根据自己的排查经验整理的。某种意义上,这个表比正文里的代码更实用,因为遇到报错时查问题原因才是常态。
| 现象 | 可能原因 | 排查思路和解决办法 |
|---|---|---|
前端报了DataError,导入密钥失败 | 公钥格式不对,或者Base64没解码干净 | 确认去掉PEM头尾、换行符;用atob前检查字符串长度是否为4的倍数;让后端换用统一的纯Base64编码 |
| 后端解密AES密文报tag mismatch | GCM的认证标签没有正确切分,或密文被截断 | 确认拆包规则:IV长度12字节,authTag最后16字节;检查传输过程是否发生了编码转换,比如=号丢失 |
| 后端能解密但内容是乱码 | 加密算法一致,但编码不一致 | 统一密文编码格式,要么都Base64,要么都hex;确认TextEncoder和Buffer之间没有默认编码不一致问题 |
| MD5签名后后端总是验签失败 | 参数排序不一致或拼接规则不一致 | 统一按ASCII字典序排序,空字段是否忽略、数组如何序列化都要在文档写清楚;建议后端导出验签代码片段供前端对照 |
| RSA加密大字段报错 | 明文长度超过密钥最大加密长度限制 | 改用混合加密:用AES加密大数据体,用RSA加密AES密钥;或者把字段拆分后分段加密(不推荐分段,复杂度高) |
| 前端无HTTPS环境下Web Crypto API报错 | 非安全上下文不支持crypto.subtle | 生产环境必须用HTTPS;本地开发用localhost可以,内网IP访问时部分浏览器也会限制 |
| 同一密钥加密相同明文得到相同密文 | IV固定或者IV生成逻辑有问题 | 每次加密都要重新生成随机IV;检查是否有全局缓存导致IV复用 |
| 签名通过但接口被重复调用 | 没有防重放机制 | 增加时间戳+nonce字段;后端对nonce做幂等校验,签名通过后再判断重复 |
最后再分享一个我自己的实操习惯:前端加密代码的调试,不要等接口联调时才做。写一个小的纯函数测试页,把“加密 -> 模拟后端解密 -> 对比结果”在这个页面上跑通,可以大幅缩短后续联调的时间。测试页里我会预置几组固定的密钥和IV,专门用来验证编解码函数;业务代码里再单独抽一个加密服务模块,所有加密逻辑集中管理。用这种方式,我过去几次项目联调中几乎都在第一轮就对上了加解密格式,剩下的时间都花在了业务逻辑上。加密这东西,看着玄学,其实把参数对齐了,剩下的都是体力活。