1. 先说为什么前端要捣鼓数据加密这件事做 Vue 项目做久了你会发现一个特别尴尬的事实前端代码本质上就是一本摊开的书任何懂点浏览器开发者工具的人都能看到你的请求参数、接口返回值甚至直接把你的 JavaScript 逻辑扒个底朝天。于是很多人就会问那前端做数据加密有什么用反正密钥都在代码里别人一翻就能看到加密不就成摆设了吗这个问题问得没毛病但忽略了一个核心现实绝大多数攻击者根本不会去看你的源码他们用的是自动化脚本、抓包工具、批量扫描器。你做个 Base64 处理就能挡住一批只会看网络面板的菜鸟你做个加盐的 SHA-256就能挡住一批直接拿明文撞接口的脚本你认真上 AES 对称加密就能让中间人抓到的包变成一堆没法直接利用的乱码。加密不是万能盾牌但它是一个基础的纵深防御环节能把攻击成本抬高一大截。这篇文章我不讲密码学论文里的高深理论就从一个 Vue 项目实战的角度把六种最常用的数据加密方式挨个过一遍底层原理用大白话解释Vue 里的集成代码直接给适合谁用、什么时候别用也说清楚。毕竟我们写业务代码的不是去设计加密算法而是要学会在合适的场景里选一把合适的锁。2. 六种加密方式的整体认知与选型地图2.1 先把加密、哈希、编码这三兄弟分清楚在我开始贴代码之前有一件事必须先掰扯清楚很多人容易把加密、哈希、编码混为一谈实际上它们完全是三类东西。编码Encoding是可逆的而且不需要密钥它的目的是格式转换而不是保护数据。Base64 就属于这一类。哈希Hashing是不可逆的同一个输入永远得到同一个输出但输出无法还原回输入它的目的是完整性校验或口令存储MD5、SHA-256 都属于这一类。加密Encryption是可逆但需要密钥的加密后的数据可以解密还原AES、RSA 才属于这一类。这个区分直接决定了你的选型方向。如果只是不想让数据长得太明显用编码如果要验证数据有没有被篡改用哈希如果问你要不要用加密那么还要细分对称加密还是非对称加密这两者的区别一句话就能说清对称加密就像一把钥匙开一把锁加解密用同一个密钥非对称加密是两把钥匙公钥加密、私钥解密公钥随便发私钥自己藏。2.2 六种方式快速总览与选型建议先把六种方式摊开给一个全局视角后面逐一说实现细节。加密方式类别可逆性典型用途强度等级Base64编码可逆无密钥图片传输、URL 参数伪装、二进制转文本极低仅防肉眼MD5哈希不可逆旧系统口令摘要、文件校验低已可碰撞SHA-256哈希不可逆签名校验、口令加盐哈希中高AES对称加密可逆需密钥请求参数加密、敏感字段加密存储高RSA非对称加密可逆需密钥对密钥交换、登录密码传输高加盐逻辑辅助手段不可逆配合 MD5、SHA-256 使用视配合对象而定选型建议很简单登录密码要么走 RSA 传公钥加密后的密文要么走 SHA-256 加盐哈希后直接交给后端接口请求体要做防篡改用 AES 时间戳 签名头URL 参数不想太扎眼用 Base64 做一层轻量伪装。下面我逐个给你拆解重点放在 Vue 工程里能直接跑起来的实现。3. Base64 与 MD5入门级方案真不是让你拿去防黑客的3.1 在 Vue 里用 Base64 处理传输数据Base64 的本质是把任意二进制数据转换成由 64 个字符组成的可打印文本它解决的问题是兼容性而不是安全性。比如你在 URL 里直接传中文参数、传图片二进制流很可能因为字符集、编码规则不同而出问题转成 Base64 之后就变成了data:image/png;base64,...这样的安全字符串。Vue 项目里做 Base64 有现成工具浏览器原生就支持两个函数btoa用来编码atob用来解码。注意坑点这两个函数不支持 Unicode 字符直接对中文调用会直接抛异常。解决方式是先做一步encodeURIComponent处理。// 支持中文的 Base64 编码/解码工具 export const base64Encode (str) { return btoa(encodeURIComponent(str)) } export const base64Decode (str) { return decodeURIComponent(atob(str)) }如果你用的是 Node 服务端渲染Nuxt/SSR或者项目里已经装了 lodash也可以用lodash的_.base64Encode不过我一般建议直接用原生封装不引入额外依赖。实测下来Base64 编码后的体积会增加大约 33%如果接口对请求体大小有严格要求可以配合压缩使用。Base64 最大的好处是随手就能用最大的问题也明显它没有密钥任何人拿到字符串都能解开。所以它最合适的场景是URL 参数里的中文转码、图片压缩后的 base64 展示、把带特殊字符的参数塞进跳转链接。我只是把它归类为加密方式里讨论但你必须明白它是所有方案里最不设防的千万别拿它当真正的安全措施。3.2 MD5 的正确用法校验或加盐而不是直接存密码MD5 曾经是哈希领域的顶流现在却是安全领域的反面教材。它的碰撞攻击已经非常成熟——两个不同内容可以算出同一个 MD5 值彩虹表更是让没加盐的 MD5 密码分分钟被反查。我在项目里见到过很多老系统数据库里存的就是用户密码的裸 MD5这类系统几乎是给攻击者送人头。那 MD5 就完全没用了吗也不是。它适合做文件完整性校验、版本号比对、缓存 key 生成等对安全性要求不高的场景。比如上传文件时前端算一个 MD5后端比对一下就能判断这个文件是否已经存在省一遍上传流量。在 Vue 里算 MD5 最常用的库是js-md5安装一句命令npm install js-md5import md5 from js-md5 const rawPassword 123456 const hashedPassword md5(rawPassword) console.log(hashedPassword) // e10adc3949ba59abbe56e057f20f883e如果你实在要在老系统里用 MD5 存口令有一个补救方案加盐Salt。盐就是一段随机字符串拼在原文后面一起做哈希。比如md5(123456 x7F2k9Qp)跟md5(123456)的结果完全两码事。这样即便攻击者拿到哈希值也无法直接用彩虹表反查出原始密码。但我的建议是新项目不要再用 MD5 存密码了最低也要上 SHA-256 加盐。MD5 充其量是你了解哈希演进的入门样本不应成为生产环境的主角。4. SHA-256 与 Web Crypto API现代哈希的正确姿势4.1 使用 crypto-js 在前端计算 SHA-256SHA-256 属于 SHA-2 家族输出固定 256 位即 64 个十六进制字符。它的安全性远高于 MD5目前没有实际可行的碰撞攻击手段是行业公认的哈希基准线。很多支付、开放平台的验签逻辑里就是用 SHA-256 对参数拼接串做签名。Vue 里最顺手的库是crypto-js它是一个老牌加密库内置了哈希、对称加密、Hmac 等一堆能力。安装方式npm install crypto-js计算 SHA-256 的代码import CryptoJS from crypto-js const message 这是一段需要哈希的文本 const digest CryptoJS.SHA256(message).toString() console.log(digest) // 输出 64 位十六进制字符串需要注意CryptoJS.SHA256返回的是一个 WordArray 对象必须调用.toString()才能拿到十六进制字符串。如果你需要的是 Base64 格式的摘要可以改用CryptoJS.enc.Base64.stringify(CryptoJS.SHA256(message))。4.2 优先使用浏览器原生 Web Crypto API除了引入 crypto-js现代浏览器其实已经内置了 Web Crypto API通过window.crypto.subtle暴露异步哈希能力完全不需要额外依赖而且底层使用原生实现性能比纯 JavaScript 库好很多。Vue 3 项目里可以这样封装// utils/sha256.js export async function sha256Hex(text) { const encoder new TextEncoder() const data encoder.encode(text) const hashBuffer await crypto.subtle.digest(SHA-256, data) const hashArray Array.from(new Uint8Array(hashBuffer)) return hashArray.map((b) b.toString(16).padStart(2, 0)).join() }使用时import { sha256Hex } from /utils/sha256 const sign await sha256Hex(rawData secretKey)这里有一个容易踩的坑Web Crypto API 的subtle属性只在安全上下文HTTPS 或 localhost中可用。你把项目部署到 HTTP 协议的测试环境crypto.subtle会是 undefined。解决方案是降级到 crypto-js或者在开发环境用 HTTPS。签名场景里SHA-256 的常见用法是参数排序 拼接密钥 哈希。比如你有{ name: 张三, age: 25 }先把 key 排序得到age25name张三再在末尾拼上约定的 secret key整体做 SHA-256得到的哈希值作为签名头传给后端。后端用同样的规则重算一遍就能判断参数有没有被篡改。5. AES 对称加密请求参数加密的主力方案5.1 理解 AES 的工作模式CBC 和 GCM 怎么选AESAdvanced Encryption Standard是当前最主流的对称加密算法密钥长度支持 128、192、256 位。它本身是一个分组密码算法加密时把数据分成固定长度的块来运算所以需要配合工作模式来使用。前端最常接触的是 CBCCipher Block Chaining和 GCMGalois/Counter Mode。CBC 模式下每个明文块在加密前会与前一个密文块做异或运算因此需要初始化向量IV来保证第一个块的安全性。GCM 模式则在加密的同时生成认证标签可以同时保证机密性和完整性。结论先行新项目优先用 GCM。它自带认证能力能发现密文被篡改而 CBC 不具备这个能力你还需要额外叠加 HMAC 做完整性校验。但在国内很多现有后端架构中CBC 依然大量存在因为一些网关联调协议写死了 CBC。下面两种模式我都贴出 Vue 集成代码。5.2 在 Vue 中用 crypto-js 实现 AES 加解密先看 CBC 模式这也是你在旧项目里最常见的写法。需要准备三个变量密钥 key、偏移量 iv、密文输出格式。密钥和 IV 的长度有讲究AES-128 对应 16 字节密钥AES-256 对应 32 字节密钥。import CryptoJS from crypto-js // 密钥和 IV 实际应从配置中心获取或由后端下发 const AES_KEY CryptoJS.enc.Utf8.parse(0123456789abcdef0123456789abcdef) const AES_IV CryptoJS.enc.Utf8.parse(abcdef9876543210) export function aesEncrypt(plaintext) { const encrypted CryptoJS.AES.encrypt(plaintext, AES_KEY, { iv: AES_IV, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }) return encrypted.toString() } export function aesDecrypt(ciphertext) { const decrypted CryptoJS.AES.decrypt(ciphertext, AES_KEY, { iv: AES_IV, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }) return decrypted.toString(CryptoJS.enc.Utf8) }这段代码有必须强调的注意点CryptoJS.AES.encrypt的第一个参数也可以是字符串如果直接传字符串库内部会把它当成 UTF-8 编码处理。而密钥和 IV 如果用字符串直接传同样会被当成 UTF-8 处理所以很多文章会建议你显式CryptoJS.enc.Utf8.parse()这是对的能避免某些 WordArray 尺寸对不上的问题。GCM 模式在 crypto-js 里用起来略有不同crypto-js 对 GCM 的支持是通过CryptoJS.AES.encrypt配合CryptoJS.mode.GCM实现的但这个库对 GCM 的成熟度不如专用库。我建议你在用 GCM 时直接上 Web Crypto APIasync function aesGcmEncrypt(plaintext, key) { const encoder new TextEncoder() const iv crypto.getRandomValues(new Uint8Array(12)) const encrypted await crypto.subtle.encrypt( { name: AES-GCM, iv }, key, encoder.encode(plaintext) ) // 需要把 iv 和密文一起传给后端因为解密时要同样的 iv return { iv: Array.from(iv).map(b b.toString(16).padStart(2, 0)).join(), ciphertext: Array.from(new Uint8Array(encrypted)) .map(b b.toString(16).padStart(2, 0)).join() } }AES 加密的实际应用场景非常多登录时对密码做 AES 加密再传输、整个请求体加密、把用户手机号身份证号等敏感字段加密后再存入前端缓存。但必须记住AES 的安全性完全取决于密钥管理。密钥藏在前端代码里只是提高了破解成本不是绝对安全。6. RSA 非对称加密敏感数据最后一道硬防线6.1 非对称加密为什么更安全RSA 是目前最经典的公钥密码体制核心思想是公钥加密、私钥解密。公钥可以公开给任何人私钥只能由持有者保管。前端用公钥加密后端用私钥解密即便前端代码被完全扒走攻击者拿到的也只是公钥没有任何解密能力。这就是 RSA 跟前端 AES 最大的区别AES 密钥一旦泄露加密就形同虚设RSA 的公钥泄露不影响加密安全。代价也很明显RSA 的加密长度受限1024 位密钥最多只能加密约 117 字节的明文2048 位密钥大约是 245 字节。所以 RSA 很少用来加密大段文本而是用于加密 AES 的密钥、登录密码等短小敏感信息。混合密码体制的标准做法是用 RSA 加密一个随机生成的 AES 密钥再用这个 AES 密钥加密真正的业务数据。6.2 Vue 里集成 jsencryptVue 项目里集成 RSA最常用的是jsencrypt这个库它封装了 RSA 加密解密的过程使用起来很简单。安装npm install jsencrypt基础用法import JSEncrypt from jsencrypt export function rsaEncrypt(plaintext, publicKey) { const encryptor new JSEncrypt() encryptor.setPublicKey(publicKey) return encryptor.encrypt(plaintext) }encrypt方法返回的是 Base64 编码的密文字符串如果加密失败会返回false所以使用时需要做判断。publicKey 一般由后端提供可能是 PKCS#1 格式也可能是 PKCS#8 格式jsencrypt 对两种都兼容但在实际对接中如果发现加密报错先检查 key 的格式有没有被转义、有没有多余的换行空格。登录场景配合使用示例import { rsaEncrypt } from /utils/rsa import { publicKey } from /config async function handleLogin(loginForm) { const encryptedPassword rsaEncrypt(loginForm.password, publicKey) const params { username: loginForm.username, password: encryptedPassword, timestamp: Date.now() } // 传给后端 await loginApi(params) }这样后端拿到的是 RSA 加密后的密码即便请求被完整截获攻击者也无法还原出原始密码。有一个实操经验某些后端对 RSA 密文的处理要求是十六进制字符串而不是 Base64。jsencrypt 默认返回 Base64如果你碰到后端密文解析失败可以通过hex2b64或b64tohex这类转换方法处理。RSA 在前端还有个经典用法验证签名。后端用私钥对一段数据签名前端用公钥验签确保数据确实来自可信的后端且没有被篡改。jsencrypt 也提供了对应的签名验签方法sign和verify。6.3 混合加密结合 AES 和 RSA 做账号体系实际项目中很少只用一种加密手段更常见的组合是RSA AES 混合加密。流程大致这样前端生成一个随机的 AES 密钥用这个 AES 密钥加密业务数据得到密文 A用后端提供的 RSA 公钥加密 AES 密钥本身得到密文 B把密文 A 和密文 B 一起发给后端后端先用 RSA 私钥解出 AES 密钥再用 AES 密钥解出业务数据这样做的好处是AES 加密速度快、适合大数据量RSA 解决了 AES 密钥的配送问题。典型的密钥交换框架就是这么运作的。我建议如果你的项目有严格的防抓包要求可以用这套混合方案但前提是前端生成的 AES 密钥必须有足够的随机性——直接用crypto.getRandomValues生成。7. 工程化封装把加密能力整合进 Vue 项目7.1 封装统一的加解密工具模块在实际 Vue 项目里不建议每个组件里直接 import CryptoJS然后各写各的加密逻辑。更好的做法是把所有加密工具收敛到一个模块统一导出方便维护和替换。以 src/utils 目录为例可以建立一个crypto.js文件把所有方法集中管理。// src/utils/crypto.js import CryptoJS from crypto-js import JSEncrypt from jsencrypt const CRYPTO_CONFIG { aesKey: 你的AES密钥, aesIv: 你的AES偏移量, rsaPublicKey: 你的RSA公钥 } const parseKey (key) CryptoJS.enc.Utf8.parse(key) export const sha256 (data) CryptoJS.SHA256(data).toString() export const aesEncrypt (data) { return CryptoJS.AES.encrypt(data, parseKey(CRYPTO_CONFIG.aesKey), { iv: parseKey(CRYPTO_CONFIG.aesIv), mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }).toString() } export const aesDecrypt (ciphertext) { const decrypted CryptoJS.AES.decrypt(ciphertext, parseKey(CRYPTO_CONFIG.aesKey), { iv: parseKey(CRYPTO_CONFIG.aesIv), mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }) return decrypted.toString(CryptoJS.enc.Utf8) } export const rsaEncrypt (plaintext) { const encryptor new JSEncrypt() encryptor.setPublicKey(CRYPTO_CONFIG.rsaPublicKey) const encrypted encryptor.encrypt(plaintext) if (encrypted false) { throw new Error(RSA 加密失败) } return encrypted }这种集中管理的模式有很多好处。一是替换底层库时不需要改动业务代码二是可以把密钥集中在一个地方做配置管理三是对外暴露的方法名语义清晰团队协作时不容易用错。7.2 在 Axios 拦截器中接入加解密逻辑加密工具封装好之后下一步就是把它接入 HTTP 请求链路。Vue 项目基本都用 Axios它的请求拦截器和响应拦截器是天然的加解密接入点。下面是一个示例对请求参数做 AES 加密放在encryptedData字段中对响应数据中的密文做自动解密。// src/utils/request.js import axios from axios import { aesEncrypt, aesDecrypt } from ./crypto const service axios.create({ baseURL: import.meta.env.VITE_API_BASE_URL, timeout: 15000 }) service.interceptors.request.use((config) { if (config.data config.encrypt) { config.data { encryptedData: aesEncrypt(JSON.stringify(config.data)) } } return config }) service.interceptors.response.use((response) { const res response.data if (res.encrypted) { return aesDecrypt(res.encryptedData) } return res })这里有两个需要留意的点。第一AES 加密后的密文是 Base64 字符串体积会膨胀所以在请求体加密时需要确认后端接收的数据结构能容纳。第二拦截器里做加解密如果密钥或 IV 配置有问题会导致所有请求失败且报错信息通常很隐晦所以封装完一定要先写单元测试打底。另外并不是所有请求都需要加密。我在实际项目中会通过一个自定义配置项encrypt来按需控制默认不加密只有需要保护敏感数据的接口才开启。这样既能保护核心数据又不会因为全量加密导致性能和 debug 难度双双在线。7.3 用 Vite 环境变量管理密钥和应用开关密钥放前端本质上是掩耳盗铃但既然业务要求加密你就得尽可能把密钥放在不容易直接看到的地方。一个常见做法是放在环境变量里Vite 项目通过import.meta.env读取。你可以创建.env.development和.env.production文件分别配置不同环境的密钥。# .env.production VITE_AES_KEYproduction_key_here VITE_AES_IVproduction_iv_here VITE_RSA_PUBLIC_KEYproduction_public_key_here代码里这样读const key import.meta.env.VITE_AES_KEY需要特别提醒.env里的内容在前端构建时会被打包进产物用户在浏览器里直接搜VITE_AES_KEY或者搜你密钥的特征字符串仍然能找到。环境变量只是帮你做环境隔离不是安全措施。对于极高安全等级的业务更稳妥的方式是在构建时从服务端动态获取密钥但那样又会引入新的复杂度需要你根据项目情况权衡。8. 请求签名与时间戳防重放比加密本身更要紧的机制8.1 不只是加密还得防时间重放加密能防窃听但防不了重放攻击。所谓重放攻击就是攻击者把抓到的合法请求原封不动地再发一遍比如你登录成功后攻击者把你的登录请求重放一次虽然他不知道密码但他可以让你的账号被反复登录。要防这个就需要引入时间戳和 nonce。具体做法是每次请求时前端生成一个唯一的 nonce随机字符串加上当前时间戳连同业务参数一起参与签名。后端收到请求后先检查时间戳是否在有效窗口期内比如 5 分钟内再检查这个 nonce 是否已经使用过最后用同样的签名算法重算签名做比对。这样既校验了参数完整性又防止了重放。8.2 前端签名流程完整代码下面是在 Vue 项目中结合 SHA-256 做请求签名的完整示例// src/utils/sign.js import { sha256 } from ./crypto export function generateSign(params, secretKey) { // 1. 去掉空值和签名本身 const sortedKeys Object.keys(params) .filter((key) key ! sign params[key] ! params[key] ! null) .sort() // 2. 拼接成 keyvaluekeyvalue const queryString sortedKeys .map((key) ${key}${params[key]}) .join() // 3. 末尾拼上密钥整体哈希 return sha256(queryString secretKey) } export function buildSignedParams(params, secretKey) { return { ...params, timestamp: Date.now(), nonce: Math.random().toString(36).slice(2), sign: } } // 构造完参数后调用 generateSign 补齐 sign实际请求中大致流程是构造业务参数插入 timestamp 和 nonce调用 generateSign 得到签名把签名塞进请求头后端用相同算法验签。我见过很多前端团队只做加密不做签名结果接口被脚本批量刷得飞起后来后端加了签名校验才解决。所以签名和加密一定是配套的它们是车子的两个轮子。9. 常见问题与避坑指南9.1 密钥管理前端密钥不是保险箱里的钱这是我必须反复强调的一个老生常谈前端的所有密钥和算法最终都会暴露给用户。无论是 AES 密钥、RSA 公钥还是加盐规则只要用户愿意都能从代码里挖出来。所以前端的加密本质是提高攻击成本而不是做到绝对安全。真正的安全需要后端配合敏感数据尽量不返回前端、核心操作强制二次验证、后端对关键接口做风控策略。如果后端工程师跟你说前端加密没啥用传明文吧你可以把攻击成本理论讲给他听但别跟他在密钥安全性上抬杠。合理姿势是承担你们该承担的防护层级同时把真正机密的东西牢牢锁在后端。9.2 编码问题中文密文解密出来是一堆乱码这是 AES 加解密里出现频率最高的问题。原因多半是解密时没有用 UTF-8 解码。crypto-js 里解密出来的 WordArray 需要调用toString(CryptoJS.enc.Utf8)转换为字符串如果省略了参数默认转成 hex 字符串自然是一堆乱码。另一个常见原因是加密前没有对原文做JSON.stringify直接把 JavaScript 对象传进去结果解密出来是[object Object]。解决方式是写一个自测用例在集成到请求链路之前先本地验证一下加解密是否往返一致。const raw JSON.stringify({ name: 张三, score: 99 }) const encrypted aesEncrypt(raw) const decrypted aesDecrypt(encrypted) console.log(decrypted raw) // 必须为 true9.3 密钥长度与算法不匹配AES-128 需要 16 字节密钥AES-256 需要 32 字节密钥。如果密钥长度不匹配crypto-js 有时候不会直接报错而是默默用错误的方式处理导致加解密结果不一致。RSA 密钥长度不匹配时jsencrypt 会直接返回 false 或抛异常。所以新项目最好在工具模块里加上长度校验逻辑发现问题提前暴露。另一个容易被忽略的点是秘钥字符集。密钥本身建议使用 ASCII 字符如果用了中文作为密钥parse 后的字节数是不可控的容易出问题。我踩过一次后来统一用固定长度的十六进制字符串作为密钥立刻安静了。9.4 加密在 Electron 或小程序等非浏览器环境中的差异Vue 不只是跑在浏览器里Vue 项目也可能被打包成 Electron 应用或者作为 H5 嵌套在小程序 WebView 里。这些环境的加密 API 可能跟浏览器不一样。Electron 是 Chromium 内核Web Crypto API 可用小程序里crypto.subtle可能在基础库版本较低时不可用。我的建议是工具层做能力检测优先使用原生 Web Crypto API检测不到就降级到 crypto-js。const supportsWebCrypto typeof crypto ! undefined crypto.subtle这样一套兼容方案下来基本能覆盖大部分场景。9.5 调试时别让加密挡住了自己的路前端加密有个副作用一旦启用浏览器的开发者工具里所有请求参数都是密文后端日志里也是密文出了问题排查起来很费劲。我的建议是环境隔离开发环境默认关闭加解密或者提供一个明文模式的开关通过环境变量控制。生产环境强制开启本地调试时关掉效率能提升一大截。另外在响应拦截器里做自动解密时如果后端返回的不是密文格式比如报错信息是纯 JSON解密逻辑会直接抛异常导致错误信息也显示不了。所以解密前一定要先判断是不是密文不是就原样放行。这个判断条件可以约定为某个字段是否存在比如response.data.encrypted true。9.6 阿里云等网关环境下的加密适配如果你把 Vue 项目部署在带有 API 网关的云环境里网关层可能已经自带加解密和签名校验能力这时候前端重复实现一套加密反而可能与网关逻辑冲突。我遇到过的情况是网关要求 CORS 请求头带自定义的签名头前端因为没在 Axios 里配置allowedHeaders导致预检请求失败接口 403。解决方式是在 Axios 默认配置里显式声明自定义头和Content-Type。这类适配问题不算加密本身的坑但确实是部署加密功能时常被绊倒的一环。10. 一个 Vue 组件里综合使用加密的小案例前面都是零散的代码片段很多人看完可能会觉得道理我都懂但组合起来还是没把握。这里我给一个完整的业务场景一个用户登录表单需要把密码用 RSA 加密传输同时给整个请求做 SHA-256 签名防止参数被篡改和重放。script setup import { reactive, ref } from vue import { aesEncrypt, rsaEncrypt, sha256 } from /utils/crypto import { loginApi } from /api/auth import { publicKey } from /config/env const form reactive({ username: , password: }) const loading ref(false) async function handleSubmit() { loading.value true try { const timestamp Date.now() const nonce Math.random().toString(36).slice(2) // 1. 密码单独用 RSA 加密 const encryptedPassword rsaEncrypt(form.password, publicKey) // 2. 构造请求参数用户名不需要高强度加密但可以用 AES 包一层 const payload { username: aesEncrypt(form.username), password: encryptedPassword, timestamp, nonce } // 3. 计算签名 const sign sha256( username${payload.username}password${payload.password}timestamp${timestamp}nonce${nonce}secretKeyyour_secret ) // 4. 调用接口 const res await loginApi({ ...payload, sign }) console.log(登录成功, res) } catch (error) { console.error(登录失败, error) } finally { loading.value false } } /script这个例子展示的是一个通用思路不同敏感级别的数据用不同强度的加密方式处理密码用非对称加密用户名用对称加密整体加签名。这套组合在大多数后台管理系统场景里已经足够用而且代码结构清晰后端也好配合。11. 我做了这么多年 Vue 项目关于加密的最后几句大实话加密这个话题前端做久了心态很容易在两个极端之间摇摆。一端是前端加密毫无意义就是脱裤子放屁另一端是我用了 AES 256 就绝对安全了。真实情况就是开头那句话前端加密从来不是铜墙铁壁它能防住的是脚本小子、批量抓包、数据在传输链路上的裸奔防不住的是真铁了心逆向你应用的人。所以设计加密方案时先别追求最高的加密算法强度先想明白你要防守的是谁他的技术水平有多高你的核心资产值不值得他花三天时间逆向。我在实际落地时的一个体会是加密方式选型不如加密流程的规范来得重要。规则一密钥配置集中管理不许在业务代码里散落规则二加解密必须做自测用一组固定数据跑通往返规则三加解密作为中间件或拦截器统一处理不给业务组件接触加密细节的机会规则四所有加密逻辑留一个可关闭的开关方便排查问题。守住这四条哪怕你用的只是 AES-CBC SHA-256 的组合在绝大多数业务场景里也已经是很稳健的一套屏障了。最后分享一个小技巧Vue DevTools 的 Network 面板里看请求参数都是密文不利于本地联调。但你可以给加密工具模块打一个条件断点当import.meta.env.DEV为 true 时在控制台同时输出明文和密文。这样一来开发时能看得懂生产环境又不泄露明文两边都不耽误。这个小操作挽救过我好多次 debug 的头发现在顺手送给你。