Postman模拟CryptoJS AES加密与Java Hutool解密联调实战
1. 项目概述:为什么需要模拟前端加密?
在前后端分离的架构里,数据安全传输是基石。我们经常遇到这样的场景:前端工程师用 CryptoJS 的 AES/CBC 模式对敏感数据(比如登录密码、支付信息)进行加密,然后通过 HTTP 请求发送给后端。后端 Java 服务则使用 Hutool 这样的工具库进行解密。听起来很标准,对吧?但问题就出在“联调”和“测试”这两个环节。
你作为后端开发,接口写好了,Hutool 的解密代码也配上了,但前端兄弟的页面还没联调好,或者你只是想用 Postman 单独测试一下这个加密接口。这时候,你发现 Postman 发出去的明文请求,后端根本解不开,因为后端期待的是密文。于是,你不得不去写一段临时的、丑陋的测试代码,或者干脆让前端先给你一个加密后的字符串硬编码进去。这既低效,又破坏了测试的完整性和真实性。
这个项目的核心价值就在这里:在 Postman 的预请求脚本(Pre-request Script)里,用 JavaScript 完整复现前端 CryptoJS 的 AES/CBC 加密逻辑。让你在 Postman 里就能模拟出前端加密后的请求体,直接发给后端 Hutool 解密接口进行测试。这不仅仅是“能调通”,更是保证两端加密解密逻辑严格一致、避免联调扯皮的关键。我见过太多团队因为一个 Padding 模式或者 IV 处理方式的细微差别,在联调阶段浪费一两天时间。通过这个实战,我们将把整个过程透明化、脚本化,实现真正的“无缝对接”。
2. 核心思路与方案选型:为何是 AES/CBC + PKCS7Padding?
在动手之前,我们必须把加密方案的选择讲清楚,这是后续一切操作的基础,也是很多开发者第一次接触时最容易混淆的地方。
2.1 为什么选择 AES/CBC 模式?
AES(高级加密标准)是目前对称加密的黄金标准,安全性和性能都经过充分验证。而 CBC(密码分组链接)模式是其最常用的一种工作模式。
- CBC 模式的工作原理:它会将明文分成固定大小的块(AES 是 128 位,即 16 字节),每个明文块在加密前,会先与前一个密文块进行异或操作。第一个块则与一个叫做“初始化向量”的随机值进行异或。这个机制使得即使完全相同的明文,每次加密也会产生完全不同的密文,有效抵御了模式分析攻击。
- CBC 的核心要求:
- 初始化向量:一个长度等于分组大小(16字节)的随机值。它不需要保密,但必须不可预测,且每次加密都应不同。通常 IV 会随密文一起传输。
- 分组对齐:明文长度必须是 16 字节的整数倍。如果不是,就需要进行“填充”。
选择 CBC 是因为它在安全性和实现复杂度之间取得了很好的平衡,并且被 CryptoJS 和 Java JCE(Java 加密体系)广泛支持,是前后端加解密对接中最常见的模式。
2.2 为什么填充方案必须是 PKCS7Padding?
因为明文长度不总是 16 字节的倍数,所以需要填充。PKCS#7 是最通用、最标准的填充方案。
- PKCS7Padding 的规则:假设需要填充
N个字节,那么这N个字节的值就都是N。例如,如果最后一个块还差 3 个字节,就填充0x03 0x03 0x03。 - 关键点:在 Java 的世界里,标准库的
Cipher类中,填充模式的名字叫PKCS5Padding。这是一个历史遗留问题。因为 PKCS#5 标准最初是为 8 字节分组设计的,而 PKCS#7 是它的泛化,适用于 1-255 字节的分组。对于 AES(16字节分组),PKCS5Padding和PKCS7Padding在算法上完全等价。所以,当你在 Java 中指定AES/CBC/PKCS5Padding时,实际执行的就是 PKCS#7 填充。
这里就引出了第一个关键对接点:CryptoJS 默认使用的就是 PKCS7 填充。因此,Java 后端必须使用AES/CBC/PKCS5Padding这个算法字符串,才能正确解密。
2.3 密钥的格式与处理:Base64 还是 Hex?
密钥是加密解密的根本。AES-128 的密钥是 16 字节,AES-192 是 24 字节,AES-256 是 32 字节。在实际传输和配置中,我们很少直接处理二进制字节,而是将其编码为字符串。
- CryptoJS 的偏好:CryptoJS 的
AES.encrypt方法通常接受一个“WordArray”对象作为密钥。为了方便,我们经常将一个 Hex(十六进制)字符串或 Base64 字符串传递给它。但关键在于,CryptoJS 会把这个字符串当作“密码”,并使用它自己的一个基于 OpenSSL 兼容的密钥派生函数(EVP_BytesToKey)来生成实际用于加密的密钥和 IV。这引入了不必要的复杂性,且与 Java 端难以匹配。 - 最佳实践(对接关键):为了确保两端密钥一致,我们应该直接使用原始的密钥字节,并将其编码为 Base64 或 Hex 字符串进行存储和传递。在 CryptoJS 中,我们可以使用
CryptoJS.enc.Base64.parse(base64KeyString)或CryptoJS.enc.Hex.parse(hexKeyString)来将字符串还原为 WordArray。在 Java 中,则使用Base64.getDecoder().decode(base64KeyString)或类似的 Hex 解码库来获取字节数组。
方案总结:为了无缝对接,我们约定:
- 加密算法:
AES/CBC/PKCS7Padding(CryptoJS) 对应AES/CBC/PKCS5Padding(Java)。 - 密钥处理:双方使用相同编码格式(推荐 Base64)的同一组密钥字节。
- IV 处理:使用随机生成的 16 字节 IV,并将其与密文一起传输(通常拼接或单独放在请求头中)。
3. 环境准备与工具解析
工欲善其事,必先利其器。我们先来明确这个实战中需要用到的核心工具及其角色。
3.1 Postman 与预请求脚本
Postman 不仅是 API 测试工具,其强大的“预请求脚本”功能让我们能在请求发出前执行 JavaScript 代码。这正是我们模拟前端加密逻辑的舞台。
- 脚本执行环境:Postman 的脚本环境基于 Node.js 的沙箱,内置了包括
CryptoJS在内的多个常用库。这意味着我们无需额外安装,可以直接在脚本中引用CryptoJS对象。 - 访问请求数据:在预请求脚本中,我们可以通过
pm.request对象获取即将发出的请求的 URL、头信息、请求体等,并对其进行修改。我们的目标就是:读取请求体中的明文数据,加密后,再写回请求体。 - 设置环境变量:为了方便管理密钥等敏感或配置信息,我们通常将它们存储在 Postman 的环境变量或全局变量中,在脚本中通过
pm.environment.get和pm.environment.set来存取。
3.2 CryptoJS 简介
CryptoJS 是一个强大的 JavaScript 加密算法库。在 Postman 中它是内置的,我们主要使用其 AES 模块。
- 核心对象:
CryptoJS.AES - 核心方法:
CryptoJS.AES.encrypt(plaintext, key, options):加密。CryptoJS.AES.decrypt(ciphertext, key, options):解密。
- 参数说明:
plaintext:需要加密的明文,可以是字符串,也可以是CryptoJS.lib.WordArray对象。key:密钥,可以是字符串、WordArray。强烈建议传递 WordArray 以避免自动密钥派生。options:一个配置对象,最重要的两个属性是:iv:初始化向量,必须是 WordArray 类型。mode:加密模式,如CryptoJS.mode.CBC。padding:填充模式,如CryptoJS.pad.Pkcs7。
3.3 Java 后端:Hutool 与 Bouncy Castle
Hutool 是一个 Java 工具类库,其hutool-crypto模块对 JDK 的加密解密 API 进行了极佳的封装,让代码更简洁。但有时仅靠 JDK 和 Hutool 还不够。
- Hutool 的 SymmetricCrypto:这是 Hutool 进行对称加密的核心类。使用
SymmetricCrypto可以很方便地指定算法、密钥和 IV 进行加解密。 - 为什么需要 Bouncy Castle?JDK 自带的加密提供者(Provider)功能有限。一个典型的问题是:JDK 默认的
AES/CBC/PKCS5Padding实现,在解密时,如果密文长度不正确(例如不是分组的整数倍),可能会抛出异常,而不是先解密再验证填充。而 Bouncy Castle(BC)提供者的行为更符合通用标准,兼容性更好,尤其是在处理来自其他语言或库(如 CryptoJS)的密文时。引入 BC 能极大提高对接成功率。 - Bouncy Castle 的引入:只需在项目中加入 BC 的依赖(如
org.bouncycastle:bcprov-jdk15to18),并在代码中静态注册Security.addProvider(new BouncyCastleProvider()),Hutool 会自动优先使用 BC 提供者。
实操心得一:依赖陷阱如果你发现 Hutool 解密总是报
BadPaddingException或其他奇怪错误,而 CryptoJS 加密看起来没问题,第一件事就是检查是否引入了 Bouncy Castle 并正确注册。这能解决 80% 的跨语言解密兼容性问题。Maven 依赖记得选对版本,与你的 JDK 版本匹配。
4. Postman 预请求脚本加密实战
现在,我们进入核心实操环节。我将一步步展示如何在 Postman 中编写预请求脚本,将请求体中的 JSON 数据自动加密。
4.1 设置 Postman 环境变量
首先,我们把密钥和 IV 等配置信息放在环境变量里,这样脚本可以读取,也便于在不同环境(测试、生产)间切换。
- 在 Postman 右上角,点击眼睛图标管理环境。创建一个新环境,比如叫 “AES-CBC-Test”。
- 添加以下变量:
aes_key_base64: 你的 AES 密钥的 Base64 编码字符串。例如,一个 16 字节的密钥:u/Guq6r1MBq31z8Twu6WUA==(可以通过在线工具生成)。aes_iv_base64: 你的初始化向量的 Base64 编码字符串。注意:为了测试可重复性,我们先使用固定 IV。在生产脚本中,IV 应该每次随机生成。例如:AAAAAAAAAAAAAAAAAAAAAA==(16个A的Base64)。
- 在发送请求前,确保在右上角下拉框中选中了 “AES-CBC-Test” 这个环境。
4.2 编写预请求脚本
在你的 Postman 请求中,切换到 “Pre-request Script” 标签页。粘贴以下脚本:
// 1. 从环境变量中获取密钥和IV (Base64格式) const keyBase64 = pm.environment.get("aes_key_base64"); const ivBase64 = pm.environment.get("aes_iv_base64"); // 2. 将Base64字符串转换为CryptoJS可识别的WordArray对象 // 这是关键步骤!直接parse Base64,确保密钥字节原样传递。 const key = CryptoJS.enc.Base64.parse(keyBase64); const iv = CryptoJS.enc.Base64.parse(ivBase64); // 3. 获取当前请求的请求体(假设是JSON格式的raw body) const requestBody = pm.request.body; if (requestBody && requestBody.mode === 'raw' && requestBody.raw) { try { const rawData = requestBody.raw; // 你可以先解析JSON,对特定字段加密,这里演示加密整个body字符串 // 注意:实际场景可能只加密body中的某个字段(如`password`) const plainText = rawData; // 4. 使用CryptoJS进行AES/CBC/PKCS7加密 const encrypted = CryptoJS.AES.encrypt(plainText, key, { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 // 明确指定PKCS7填充 }); // 5. 加密结果是一个CipherParams对象,我们需要获取密文的Base64字符串形式 const cipherTextBase64 = encrypted.ciphertext.toString(CryptoJS.enc.Base64); // 6. 将加密后的密文,更新回请求体 // 通常,后端期望接收一个JSON对象,里面包含密文字段,例如:{"data": "加密后的Base64字符串"} const newBody = { encryptedData: cipherTextBase64, // 如果IV不是固定的,也需要传给后端,例如: // iv: ivBase64 }; // 将新的JSON对象设置为请求体 pm.request.body.update({ mode: 'raw', raw: JSON.stringify(newBody) }); // 可选:修改Content-Type头为application/json pm.request.headers.upsert({ key: 'Content-Type', value: 'application/json' }); console.log('加密成功,密文(Base64):', cipherTextBase64); console.log('新的请求体:', JSON.stringify(newBody)); } catch (error) { console.error('加密过程中发生错误:', error); // 可以选择不修改请求体,让请求以明文发出,或者抛出错误 } } else { console.warn('请求体不是raw格式或为空,跳过加密。'); }脚本逐行解析与注意事项:
- 第2步(密钥转换):
CryptoJS.enc.Base64.parse是灵魂。它确保了我们将 Base64 编码的密钥字节直接作为加密密钥使用,而不是让 CryptoJS 把字符串当成密码去派生。这是与 Java 端对齐的最关键一步。 - 第4步(加密选项):我们显式指定了
mode和padding,避免依赖 CryptoJS 可能变化的默认配置,让行为更确定。 - 第6步(请求体构造):这里演示了将整个原始请求体字符串加密。但更常见的场景是只加密 JSON 中的某个敏感字段。例如,你的请求体原是
{"username":"zhangsan","password":"123456"},你只加密password字段。脚本需要先JSON.parse(rawData),然后加密password的值,再组装新的 JSON。这需要根据你的后端接口契约来调整。 - IV 的传输:如果使用随机 IV,必须将 IV 也传给后端,因为解密时需要相同的 IV。通常有两种方式:1) 将 IV 的 Base64 字符串也放在 JSON 请求体中;2) 将 IV 放在一个自定义的 HTTP 头里(如
X-IV)。后端需要从对应位置取出。
实操心得二:调试技巧Postman 的
console.log输出可以在底部的 “Console” 标签页查看。务必在编写脚本时打开 Console,观察密钥、IV、明文、密文的转换过程,确保每一步都符合预期。特别是检查key和iv转换后的 WordArray 的sigBytes属性,确认是 16(128位)或 32(256位)。
4.3 测试脚本效果
- 在请求的 “Body” 标签页,选择 “raw” 和 “JSON”,输入你的明文 JSON,例如:
{"username": "testUser", "password": "mySecretPassword"}。 - 确保预请求脚本已保存。
- 点击 “Send” 发送请求。
- 在 Postman 的 “Console” (View -> Show Postman Console) 中,你应该能看到 “加密成功” 的日志。
- 切换到 “Request” 部分查看实际发出的请求,你会发现 Body 已经变成了类似
{"encryptedData": "U2FsdGVkX1/...很长一串Base64..."}的形式。
至此,Postman 端的加密模拟已经完成。你发出的每一个请求,其请求体都已经是前端 CryptoJS 加密后的标准格式。
5. Java (Hutool) 后端解密实现
前端模拟好了,后端必须能正确解密。我们使用 Hutool 来简化代码,并引入 Bouncy Castle 提升兼容性。
5.1 项目依赖准备
在你的pom.xml中添加以下依赖:
<dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.8.16</version> <!-- 请使用最新稳定版 --> </dependency> <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk15to18</artifactId> <version>1.72</version> <!-- 请使用最新稳定版 --> </dependency>5.2 解密工具类编写
创建一个AesCbcUtil工具类,封装解密逻辑。
import cn.hutool.core.codec.Base64; import cn.hutool.crypto.Mode; import cn.hutool.crypto.Padding; import cn.hutool.crypto.symmetric.AES; import org.bouncycastle.jce.provider.BouncyCastleProvider; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import java.nio.charset.StandardCharsets; import java.security.Security; public class AesCbcUtil { static { // 静态注册BouncyCastle提供者,确保优先使用 Security.addProvider(new BouncyCastleProvider()); } /** * 使用Hutool AES工具解密 (CBC模式, PKCS7Padding) * * @param base64Key Base64编码的密钥 * @param base64Iv Base64编码的初始化向量 * @param base64Data Base64编码的密文 * @return 解密后的明文字符串 */ public static String decryptWithHutool(String base64Key, String base64Iv, String base64Data) { // 1. 解码Base64得到字节数组 byte[] keyBytes = Base64.decode(base64Key); byte[] ivBytes = Base64.decode(base64Iv); byte[] encryptedBytes = Base64.decode(base64Data); // 2. 构建AES对象,指定CBC模式和PKCS5Padding(对应PKCS7) // Hutool 内部会使用我们注册的BouncyCastle提供者 AES aes = new AES(Mode.CBC, Padding.PKCS5Padding, new SecretKeySpec(keyBytes, "AES"), new IvParameterSpec(ivBytes)); // 3. 解密并返回字符串 byte[] decryptedBytes = aes.decrypt(encryptedBytes); return new String(decryptedBytes, StandardCharsets.UTF_8); } /** * 解密HTTP请求体(假设请求体为JSON,包含encryptedData字段) * 这是一个Spring MVC Controller中的示例方法 */ /* @PostMapping("/decrypt-endpoint") public ResponseEntity<?> handleEncryptedRequest(@RequestBody Map<String, String> requestBody) { String encryptedDataBase64 = requestBody.get("encryptedData"); // 从配置或环境变量获取密钥和IV String keyBase64 = "u/Guq6r1MBq31z8Twu6WUA=="; String ivBase64 = "AAAAAAAAAAAAAAAAAAAAAA=="; try { String decryptedText = decryptWithHutool(keyBase64, ivBase64, encryptedDataBase64); // 将解密后的文本解析为实际的业务对象 // ObjectMapper mapper = new ObjectMapper(); // MyRequestDto dto = mapper.readValue(decryptedText, MyRequestDto.class); // ... 处理业务逻辑 ... return ResponseEntity.ok("解密成功: " + decryptedText); } catch (Exception e) { log.error("解密失败", e); return ResponseEntity.badRequest().body("解密失败: " + e.getMessage()); } } */ }代码关键点解析:
- 静态注册 BouncyCastle:在类加载时注册,确保整个应用生命周期内生效。这是避免
BadPaddingException等兼容性问题的关键。 - Hutool AES 构造器:
new AES(Mode.CBC, Padding.PKCS5Padding, ...)这里明确指定了模式和填充。注意是PKCS5Padding,与 CryptoJS 的Pkcs7对应。 - 密钥和 IV 规范:使用
SecretKeySpec和IvParameterSpec包装解码后的字节数组,这是 JCE 的标准做法。 - 解密流程:
aes.decrypt()方法接收密文字节数组,返回明文字节数组。我们再将其转为 UTF-8 字符串。
5.3 处理只加密部分字段的场景
很多时候,我们只加密请求体中的某个字段(如password)。此时,后端接收到的 JSON 结构可能是:
{ "username": "testUser", "encryptedPassword": "U2FsdGVkX1/...", "iv": "AAAAAAAAAAAAAAAAAAAAAA==" }后端的解密逻辑就需要调整:
// 在Controller方法中 String username = requestBody.get("username"); String encryptedPasswordBase64 = requestBody.get("encryptedPassword"); String ivBase64 = requestBody.get("iv"); // 如果IV是前端随机生成并传来的 String decryptedPassword = decryptWithHutool(keyBase64, ivBase64, encryptedPasswordBase64); // 然后使用 username 和 decryptedPassword 进行后续逻辑6. 联调与常见问题排查实录
即使按照上述步骤操作,第一次对接也难免遇到问题。下面是我在多次对接中总结的“排坑指南”。
6.1 问题一:解密失败,报错javax.crypto.BadPaddingException: Given final block not properly padded
可能原因 1:密钥不一致。这是最常见的原因。
- 排查:在 Postman Console 和 Java 后端日志中,分别打印出密钥和 IV 的Base64 字符串和字节数组长度。确保两者完全一致。检查 Postman 环境变量名是否正确,是否有空格等不可见字符。
- 技巧:在 Postman 脚本开头加
console.log('Key Base64:', keyBase64, 'Length:', CryptoJS.enc.Base64.parse(keyBase64).sigBytes);。在 Java 端加log.info("Key Bytes Length: {}", Base64.decode(base64Key).length);。AES-128 长度应为 16,AES-256 应为 32。
可能原因 2:IV 不一致或未传递。
- 排查:同上,检查 IV 的 Base64 字符串和字节长度(必须是 16)。确认前端是否将 IV 传给了后端,后端是否从正确的位置(请求体或 Header)取出了 IV。
可能原因 3:加密模式或填充模式不匹配。
- 排查:确认 CryptoJS 使用了
CBC模式和Pkcs7填充。确认 Java Hutool 使用了CBC模式和PKCS5Padding。一个字母都不能错。
- 排查:确认 CryptoJS 使用了
可能原因 4:密文在传输过程中被修改或编码问题。
- 排查:检查 Postman 发出的请求体中的
encryptedData字符串,和 Java 后端通过requestBody.get(“encryptedData”)收到的字符串是否完全一致。特别注意 URL 编码问题。如果密文作为 URL 参数传递,需要确保没有发生额外的编码/解码。最佳实践是始终通过 Request Body 传输密文,并使用 Base64 编码。
- 排查:检查 Postman 发出的请求体中的
6.2 问题二:解密出的明文是乱码
- 可能原因:字符编码不一致。
- 排查:CryptoJS 加密的明文是字符串,它默认使用 UTF-8 吗?实际上,CryptoJS 的
encrypt方法接受字符串时,会使用一种特定的编码(通常是 Latin1 或 UTF-8,但行为可能因版本而异)。最稳妥的方式是,在 CryptoJS 加密前,将字符串明确转换为CryptoJS.enc.Utf8.parse(plainText),得到一个 WordArray 再加密。在 Java 端解密后,使用new String(decryptedBytes, StandardCharsets.UTF_8)指定 UTF-8 解码。 - 修正方案: Postman 脚本:
// 将明文字符串转为UTF-8的WordArray const plainTextWordArray = CryptoJS.enc.Utf8.parse(plainTextString); const encrypted = CryptoJS.AES.encrypt(plainTextWordArray, key, { iv: iv, mode: CryptoJS.mode.CBC });
- 排查:CryptoJS 加密的明文是字符串,它默认使用 UTF-8 吗?实际上,CryptoJS 的
6.3 问题三:Postman 脚本报错CryptoJS is not defined
- 可能原因:Postman 的沙箱环境问题,极少数情况下 CryptoJS 对象未加载。
- 排查:在脚本最开头加
console.log(typeof CryptoJS, typeof CryptoJS.AES)看看。 - 解决:重启 Postman,或者检查 Postman 版本。通常内置的 CryptoJS 是稳定的。
- 排查:在脚本最开头加
6.4 问题四:Java 端报java.security.InvalidKeyException: Illegal key size
- 可能原因:使用了 AES-256,但 JDK 默认的受限策略文件限制了密钥长度。
- 排查:确认密钥长度是 32 字节(256位)。
- 解决:
- 推荐方案:换用 AES-128(16字节密钥),安全性对大多数场景已足够。
- 安装 Java 无限制强度管辖策略文件(JCE)。去 Oracle 官网下载对应你 JDK 版本的
local_policy.jar和US_export_policy.jar,替换掉$JAVA_HOME/jre/lib/security/下的同名文件。
6.5 问题速查表
| 现象 | 可能原因 | 排查步骤 |
|---|---|---|
BadPaddingException | 1. 密钥/IV不一致 2. 密文被破坏 3. 模式/填充不匹配 | 1. 对比两端密钥/IV的Base64和长度 2. 对比原始密文和接收到的密文 3. 确认两端算法字符串 |
| 解密结果乱码 | 字符编码问题 | 1. CryptoJS端使用Utf8.parse2. Java端使用 UTF_8解码 |
InvalidKeyException | JDK策略限制 | 1. 检查密钥长度 2. 安装JCE无限制策略文件 |
| Postman脚本不执行 | 脚本语法错误/环境未选 | 1. 查看Postman Console错误信息 2. 确认正确环境被选中 |
7. 安全增强与生产环境建议
上面的示例为了演示清晰,使用了固定的 IV。但在生产环境中,这是不安全的。CBC 模式要求每次加密使用随机、不可预测的 IV。
7.1 在 Postman 中实现随机 IV
修改预请求脚本,每次请求生成随机 IV:
// 生成16字节的随机IV (CryptoJS.lib.WordArray) const randomIv = CryptoJS.lib.WordArray.random(16); const ivBase64 = CryptoJS.enc.Base64.stringify(randomIv); // 将本次使用的IV存入环境变量或局部变量,以便放在请求中传给后端 pm.environment.set("current_random_iv", ivBase64); // 或者直接构造在请求体中 const newBody = { encryptedData: cipherTextBase64, iv: ivBase64 // 将随机IV传给后端 };7.2 后端适配随机 IV
后端解密时,不再从固定配置读取 IV,而是从请求中提取:
// 从请求体Map中获取IV String ivBase64FromRequest = requestBody.get("iv"); String decryptedText = decryptWithHutool(keyBase64, ivBase64FromRequest, encryptedDataBase64);7.3 密钥管理
- 绝对不要将密钥硬编码在代码或 Postman 脚本中。
- Postman 环境变量中的密钥应仅为测试使用。生产环境的密钥应通过安全的配置中心(如 Apollo, Nacos)或 KMS 服务获取。
- 可以考虑将 Postman 的集合和环境变量导出为 JSON 文件,但务必通过
.gitignore排除或加密存储,避免密钥泄露。
7.4 考虑使用更现代的模式
对于新项目,可以考虑比 CBC 更安全的模式,如GCM (Galois/Counter Mode)。GCM 同时提供加密和认证功能,可以防止密文被篡改。不过,CryptoJS 和 Hutool 对 GCM 的支持度可能不如 CBC 广泛,对接时需要额外注意。如果选择 GCM,需要处理“认证标签”的传递。
整个流程走下来,从 Postman 脚本的编写到 Hutool 后端的对接,核心就在于细节的匹配:密钥字节、IV、算法模式、填充方式、字符编码。只要这五个要素在前后端完全一致,加解密过程就能像齿轮一样严丝合缝地转动。这个实战方案不仅能用于调试,其脚本和工具类稍加改造,也可以作为前端加密 SDK 和后端解密工具的标准参考实现,确保整个数据链路的安全与可靠。
