ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Vue项目数据加密实战:Base64、AES、RSA等六种方案详解

Vue项目数据加密实战:Base64、AES、RSA等六种方案详解 1. 先撕开一个真相Vue项目里的数据加密到底在防谁看到“贰零贰”这个编号应该有不少人是从这个系列一直跟过来的老读者。前面聊过 Vue 路由、状态管理、组件通信这一期想换个没那么“爽”但非常扎实的话题Vue 项目里的数据加密。严格来说前端加密是个容易被说玄乎、也容易被说没用的东西。很多后端同事一看你把加密写在页面里第一反应是“代码都在浏览器上加密等于没加密”也有一些前端同学把 Base64 当成加密登录密码转一下就直接发出去还以为很安全。这两种说法都只对了一半。前端加密的核心价值不是“绝对安全”而是“提高攻击成本、错开信任边界”。在做任何加密改造之前先想明白一个问题我到底在防谁1.1 大部分项目里的“黑客”没你想的那么强真实项目里最常见的风险不是有组织攻击而是三类人第一类是运营同学开着 DevTools 看接口返回顺手把手机号、身份证号导走了第二类是用户自己抓包改参数把会员等级、优惠金额改了再提交第三类是脚本在非官方环境里自动刷接口、批量拉数据。这三类攻击者都有一个共同点他们不会去逆向你的 JavaScript 压缩代码也不会去 hook 你的 WebAssembly。他们只是顺着浏览器就能看到的请求或者 localStorage 内容顺手捞数据。前端加密防的正是这种“顺手牵羊”。如果攻击者真的具备逆向整个前端打包产物、动态调试代码的能力那我建议你别把核心机密放在前端判断直接走后端权限校验比任何加密都靠谱。1.2 四个真实场景密码、手机号、请求参数、Token在 Vue 项目里最值得做数据保护的场景翻来覆去就那么几个。第一个是登录密码。哪怕全程走 HTTPS很多接口日志、运维平台、网管系统还是可能把请求体记录下来明文密码一旦出现在日志里就是事故。第二个是用户手机号、身份证号、家庭地址这类敏感字段它们通常会在列表页、详情页、导出报表中出现前端拿到密文后解密展示比后端直接返回明文要稳妥一些。第三个是请求参数防篡改比如下单金额、积分变动、订单状态这类数据光加密其实不够还需要签名。第四个是 Token 和用户信息在本地存储时的“静态保护”虽然不完美但至少能让普通人不至于打开 localStorage 就一眼看到完整票据。这四个场景对应的方法并不相同所以不要指望一种手段通吃。密码加密常用 RSA 或者加盐哈希敏感字段展示常用 AES请求防篡改常用 MD5 或 HMAC 签名本地存储保护可以配合 AES。1.3 前端加密解决不了的事HTTPS、后端校验、敏感数据不下发很多文章会把前端加密吹得非常全面但我建议你对它有清醒的认知。前端加密永远替代不了三件事第一替代不了 HTTPS。如果网站还在用 HTTP那你前端不管做多少层加密中间人都可以直接改你的页面代码把加密函数换成明文读取。HTTPS 是所有应用层加密的前提不是可选项。第二替代不了后端校验。前端加密得再漂亮后端也要重新解密、校验权限、做风控不能因为“前端已经加密了”就放松接口层防护。第三替代不了“不下发数据”这个原则。如果一个人根本不需要看到手机号那就不该把手机号给他而不是给一个密文再让他解密。加密只是在必须传输数据时降低泄露风险不该成为过度收集数据的挡箭牌。想清楚这些再看下面六种常用方案就不会陷入“哪个算法最安全”的纠结了。算法没有绝对安全只有适不适合当前场景。2. 六种常用方案横向对比编码、摘要、加密别混为一谈编程社区有个老梗把 Base64 叫加密的人会被安全工程师拉黑。严格讲下面六种方案里只有 AES 和 RSA 是真正的加密算法Base64 是编码MD5、SHA-256、HMAC 是摘要和签名。但在实际前后端联调中它们都被归在“数据保护手段”这个篮子里。先摆一张对比表后面再逐个拆开讲。方案类别是否可逆是否需要密钥常见用途Vue 侧常用工具Base64编码可逆不需要二进制转文本、URL 安全传输、图片压缩后上传btoa / TextEncoder / base64urlMD5摘要不可逆不需要文件校验、接口加签参数、防篡改crypto-jsSHA-256摘要不可逆不需要消息摘要、密码哈希配合盐crypto-js / Web CryptoHMAC-SHA256带密钥摘要不可逆需要共享密钥接口签名、防重放、身份认证crypto-jsAES对称加密可逆需要同一密钥敏感字段加密传输、本地数据加密crypto-jsRSA非对称加密可逆需要公私钥对登录密码、支付信息、密钥交换jsencrypt看这张表你至少应该记住三个层次。编码层Base64 只是换了表示形式随便找在线工具就能解出来它解决的是“二进制数据能不能在文本协议里跑”的问题不解决保密。摘要层MD5、SHA-256 是单向的原文变成一串固定长度字符后无法还原用来验证“这份数据有没有被人改过”但不能用来传递机密内容。加密层AES 和 RSA 是可逆的AES 快、适合大数据量RSA 慢、适合短内容和密钥交换。2.1 为什么前端团队常把“编码、摘要、加密”混着说这不是能力问题而是因为在接口调试过程中后端同事说的“数据加密”经常泛指所有“不能直接看到明文”的处理。比如登录接口要求密码先做 MD5 再加盐后端说这是加密图片上传要求 Base64后端说这是加密支付回调要求 RSA 签名后端还说这是加密。久而久之前端这边也就统一叫“加密方式”了。理解这个背景之后你在选型时至少能问出正确的问题“你要的是可逆的加密还是不可逆的签名”如果后端说“前端加密后端解密”那就是 AES 或 RSA如果后端说“后端验签”那就是 MD5、SHA 或 HMAC。这个问题能帮你少走一半弯路。3. 六种方式逐个落地代码、场景与细节下面这部分是重头戏。我会按 Base64、MD5、SHA-256、HMAC-SHA256、AES、RSA 的顺序逐个给出可直接复制的代码并解释每个方案在 Vue 项目里的真实定位。建议先在项目中安装好依赖npm install crypto-js jsencrypt npm install -D types/crypto-js3.1 Base64先给“编码”洗清冤屈Base64 的原理很简单把每 3 个字节的二进制数据转换成 4 个可打印字符。它适合处理图片上传、文件流、URL 参数这类场景。Vue 项目里最常见的两个用法是图片压缩后转 Base64 预览以及把接口返回的二进制文件流转成可下载的链接。直接使用浏览器内置的btoa和atob会有一个坑它只能处理 Latin1 字符遇到中文直接抛异常。所以在 Vue 项目里最好封装一层 UTF-8 安全的转换。// utils/base64.js export function utf8ToBase64(input) { const bytes new TextEncoder().encode(input) let binary bytes.forEach((byte) { binary String.fromCharCode(byte) }) return btoa(binary) } export function base64ToUtf8(input) { const binary atob(input) const bytes Uint8Array.from(binary, (char) char.charCodeAt(0)) return new TextDecoder().decode(bytes) }这段代码用TextEncoder先把字符串转成 UTF-8 字节数组再手动拼成二进制字符串交给btoa反过来用TextDecoder解码中文就不会乱码了。还有一类场景是把 Base64 放进 URL比如分享链接里带参数。标准 Base64 包含、/、在 URL 里会被转义所以需要替换成 URL-safe 形式换成-/换成_去掉末尾的。这个细节很多后端也会忽略联调时最容易出现“前端传过去 403、后端说签名不对”的问题。我的建议是直接用base64url这个 npm 包或者自己封装一个 replace 函数别在业务代码里到处补转义逻辑。3.2 MD5做校验和或短签名别拿来做密码存储MD5 因为碰撞漏洞已经不适合做高安全场景的密码哈希了但在接口加签、文件完整性校验这些场景里它依然非常常见原因是速度快、实现简单、后端生态支持广。在 Vue 项目里我一般只用它做两件事一是本地计算文件或字符串的校验和二是和后端约定一个简单的“参数排序拼接 MD5”签名算法。import CryptoJS from crypto-js // 单字符串 MD5 export function md5(content) { return CryptoJS.MD5(content).toString() } // 接口签名参数排序后拼接再加上盐 export function md5Sign(params, secret) { const query Object.keys(params) .filter((key) params[key] ! undefined params[key] ! null) .sort() .map((key) ${key}${encodeURIComponent(params[key])}) .join() return CryptoJS.MD5(${query}key${secret}).toString() }这段代码有几个经验点值得说明。第一Object.keys(params).sort()是为了让前后端用同一个字符串拼签名否则对象键的顺序一变签名就对不上。第二encodeURIComponent必须加否则参数里有中文、特殊符号时前后端 encode 结果不一致签名就会校验失败。第三toString()默认输出 32 位小写十六进制如果后端约定的是大写记得.toString().toUpperCase()而且这个大小写约定一定要写在接口文档里。很多人问“MD5 加盐是不是就安全了”。前端做 MD5 加盐主要作用是防彩虹表因为攻击者拿不到盐值很难反查原文但它仍然不是可逆加密后端拿到后也没法还原出原文。如果你要处理登录密码我更推荐的做法是前端用 HTTPS 传输后端在服务端用 bcrypt 或 argon2 这类慢哈希去存储前端不要承担“安全存储密码”的责任。3.3 SHA-256更稳的摘要前端也能轻松调SHA-256 是 SHA-2 家族里最常见的成员输出固定 256 位64 位十六进制字符碰撞难度比 MD5 高很多。现在很多开放平台的接口签名已经从 MD5 升级到 SHA-256前端用 crypto-js 调用非常直接。import CryptoJS from crypto-js export function sha256(content) { return CryptoJS.SHA256(content).toString() }如果你不想引入额外的加密库浏览器原生也提供了 Web Crypto API而且性能更好。要注意的是crypto.subtle只在安全上下文里可用也就是 HTTPS 页面或 localhost线上 HTTP 环境会拿不到这个对象。export async function sha256Hex(content) { const data new TextEncoder().encode(content) const hash await crypto.subtle.digest(SHA-256, data) return Array.from(new Uint8Array(hash)) .map((b) b.toString(16).padStart(2, 0)) .join() }这段代码的返回结果和 crypto-js 的sha256().toString()是一样的都是小写十六进制字符串。padStart(2, 0)是必须的因为b.toString(16)在数值小于 16 时只会输出一位不及时补零会导致哈希串变短。SHA-256 在前端还有一个实用场景计算本地文件的哈希值用于上传前校验、对比两个文件是否相同。使用input[typefile]拿到 File 对象后把arrayBuffer()的结果交给crypto.subtle.digest就行比把文件整个读成 Base64 再比较更省内存。3.4 HMAC-SHA256带密钥的签名接口防重放的好帮手如果说 SHA-256 是“不带钥匙的摘要”HMAC-SHA256 就是“带钥匙的摘要”。它需要一个客户端和服务端共享的密钥对消息计算 MAC 值。没有密钥的人即使知道消息内容也无法伪造合法的签名。这个特性让它非常适合做接口签名。举个例子前端请求下单接口时把timestamp、nonce、请求参数一起用 HMAC-SHA256 算出一个签名放到 Header 里。后端收到后用同样的共享密钥、同样的参数顺序重新计算签名如果一致就说明请求没有被篡改而且因为带了时间戳还能拦截一部分重放攻击。import CryptoJS from crypto-js const signSecret import.meta.env.VITE_SIGN_SECRET export function buildSignature(params, timestamp) { const query Object.keys(params) .sort() .map((key) ${key}${encodeURIComponent(params[key])}) .join() const raw ${timestamp}${query} return CryptoJS.HmacSHA256(raw, signSecret).toString() }调用示例const params { orderId: 123456, amount: 99.9 } const timestamp Math.floor(Date.now() / 1000).toString() const signature buildSignature(params, timestamp) // 放到 axios 请求头里 { headers: { X-Timestamp: timestamp, X-Signature: signature } }后端校验时会从 Header 里取出X-Timestamp拼接参数后重新计算签名再比较。这里有一个很容易踩的坑如果请求体是 JSONconfig.data对象的键顺序在传输过程中可能变化所以不要对整个 JSON 对象做字符串拼接签名。更稳妥的做法是只挑参与签名的核心字段放进一个稳定对象或者在接口文档里明确“参与签名字段列表”而不是“全部参数”。3.5 AES真正的可逆加密前后端必须对齐参数AES 是真正的对称加密算法加解密用同一把密钥。它速度快适合加密手机号、身份证、详细地址这类结构化字段。Vue 项目里最常用的 AES 代码是用 crypto-js 实现 AES-128-CBC 或 AES-256-CBC。先看一段可运行的代码import CryptoJS from crypto-js // 理论上应该是 16 字节128位、24 字节192位或 32 字节256位 const key CryptoJS.enc.Utf8.parse(import.meta.env.VITE_AES_KEY) const iv CryptoJS.enc.Utf8.parse(import.meta.env.VITE_AES_IV) export function aesEncrypt(plaintext) { return CryptoJS.AES.encrypt(CryptoJS.enc.Utf8.parse(plaintext), key, { iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }).toString() } export function aesDecrypt(ciphertext) { const bytes CryptoJS.AES.decrypt(ciphertext, key, { iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }) return bytes.toString(CryptoJS.enc.Utf8) }这段代码里有几个点必须强调。第一key和iv必须是用CryptoJS.enc.Utf8.parse()处理过的 WordArray而不是普通字符串。很多人图省事直接CryptoJS.AES.encrypt(hello, my-secret)这种写法 CryptoJS 会把第二个参数当成“口令”用 OpenSSL 的 KDF 派生密钥输出结果会带着Salted__前缀。倒不是说这样不安全但后端如果默认是“我直接把密钥转字节数组来解”两边怎么都对不上。第二CBC 模式下 IV 长度固定是 16 字节也就是 16 个英文字符或 16 个 UTF-8 字节。如果你用的是中文密钥看起来是 8 个字实际可能是 24 字节很容易和后端沟通出偏差。我的建议是AES 密钥和 IV 都用“长度为 16 的 ASCII 字符串”别用中文省掉一堆编码问题。第三padding 也容易打架。Java 里的AES/CBC/PKCS5PaddingPython 里的AES.MODE_CBC配合padPHP 的OPENSSL_RAW_DATA和前端 CryptoJS 的Pkcs7实际上是同一套填充规则。但如果你在 CryptoJS 里用了ZeroPadding后端还按 PKCS5 来解最后一段数据就会多出几个\x00字符轻则字符串后面有空格重则 JSON.parse 直接报错。进阶一点的做法是不要固定 IV每次加密时随机生成一个 16 字节的 IV然后把它附在密文前面一起传给后端。这样做的好处是相同明文每次密文都不同能避免固定 IV 带来的统计分析风险。代码可以这样调整export function aesEncrypt(plaintext) { const iv CryptoJS.lib.WordArray.random(16) const encrypted CryptoJS.AES.encrypt(CryptoJS.enc.Utf8.parse(plaintext), key, { iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }).toString() return iv.toString(CryptoJS.enc.Hex) : encrypted }后端解密时先按:分割取前 32 个十六进制字符还原 IV再解后面的密文。这个模式在真实项目中非常常见如果你对接的后端没有明确说“IV 写死”优先用这种随机 IV 的方案。3.6 RSA公私钥分离Vue 只留公钥RSA 是非对称加密公钥加密、私钥解密。在 Vue 项目里前端只保留公钥私钥永远放在后端服务器这样即使前端代码被完整打包攻击者也只能拿到公钥没法解密别人的数据。这个特性让它成为登录密码、支付信息这类“短小又极其敏感”字段的首选。使用 jsencrypt 这个库代码非常简单import JSEncrypt from jsencrypt const publicKey import.meta.env.VITE_RSA_PUBLIC_KEY export function rsaEncrypt(content) { const encryptor new JSEncrypt() encryptor.setPublicKey(publicKey) const result encryptor.encrypt(content) if (result false) { throw new Error(RSA 加密失败请检查公钥格式和内容长度) } return result }用起来就是rsaEncrypt(password)然后把返回值放到请求体里。需要提醒的是setPublicKey里的 PEM 字符串不能有意外换行和空格最好从环境变量读取时就用反引号完整粘贴例如VITE_RSA_PUBLIC_KEY-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA... -----END PUBLIC KEY-----如果你用的是 Viteimport.meta.env读取多行字符串可能会吞掉换行导致 JSEncrypt 无法解析。一个稳一点的办法是不要在.env里直接放多行公钥而是用一个常量文件保存或者先测试环境变量读取结果里有没有\n再决定要不要在后端环境里换成单行转义形式。RSA 还有一个绕不开的约束加密长度限制。按 PKCS#1 v1.5 填充1024 位密钥最多只能加密 117 字节2048 位密钥最多只能加密 245 字节。如果你试图用 RSA 去加密一整个几十行的 JSON 对象大部分情况下都会失败。碰到这种场景正确做法不是盲目加长密钥而是做混合加密前端随机生成一个 AES 密钥用 RSA 公钥加密这个 AES 密钥再用 AES 加密业务数据最后把两段密文一起传给后端。这个方案兼顾了 RSA 的安全性验证和 AES 的高效传输很多开放平台就是这么干的。4. 从“能用”到“好用”密钥管理、请求签名与 axios 改造上面六种方案单独跑通只是第一步。真正进入项目你会发现最麻烦的不是算法本身而是密钥放哪、签名怎么统一加、什么时候解密、日志怎么不泄露明文。4.1 密钥别写死在组件里用环境变量和构建配置隔离我见过太多 Vue 项目把 AES 密钥直接写成const key abcdef1234567890放在login.vue顶部。这样做不是不能用而是目录一乱、项目多人协作后密钥就随着代码 review 和截图流传得到处都是。更好的做法是把它统一放进环境变量。Vite 项目在根目录创建.env.development和.env.productionVITE_AES_KEY1234567890abcdef VITE_AES_IVabcdef1234567890 VITE_SIGN_SECRETyour-sign-secret VITE_RSA_PUBLIC_KEY-----BEGIN PUBLIC KEY-----组件里通过import.meta.env.VITE_AES_KEY读取。Vue CLI 项目则要用VUE_APP_前缀读法是process.env.VUE_APP_AES_KEY。请注意环境变量不增加任何“真正”的安全性。Webpack 和 Vite 打包时会把import.meta.env.VITE_*替换成真实值打进去浏览器开发者工具只要搜索VITE_AES_KEY或密钥值本身还是能挖出来。它真正解决的是“密钥散落在业务代码里”的维护问题以及“环境不同密钥不同”的部署问题。如果你的场景真的要求密钥不可见那就不要在前端做对称加密改用 RSA 公钥加密或者直接把敏感操作挪到后端。4.2 axios 拦截器统一加签六种方法如果散落在各个页面里每个页面都要记得调一遍加密函数迟早会有遗漏。我的习惯是做一个独立的utils/crypto.js然后在 axios 拦截器里统一处理。假设后端要求每个请求带X-Timestamp和X-Signature拦截器里就可以这样写import axios from axios import { buildSignature } from /utils/crypto const service axios.create({ baseURL: /api, timeout: 10000 }) service.interceptors.request.use((config) { const timestamp Math.floor(Date.now() / 1000).toString() // 这里只对参与签名的字段排序拼接避免 JSON 键顺序不稳定 const params { ...config.params, ...(config.data || {}), timestamp } config.headers[X-Timestamp] timestamp config.headers[X-Signature] buildSignature(params, timestamp) return config })注意config.headers[X-Timestamp]必须是一个字符串后端很多框架会用 Long 接收时间戳但 Header 值本身只能是字符串所以别把timestamp写成数字。如果后端部分接口返回的是 AES 加密报文响应拦截器里可以统一解密service.interceptors.response.use((response) { const { data } response if (data data.encrypted) { const decrypted aesDecrypt(data.ciphertext) response.data JSON.parse(decrypted) } return response })这里有一个容易翻车的点有些团队喜欢在拦截器里打印响应日志方便排查问题结果把解密后的明文手机号、明文订单金额全部打到控制台和日志平台上了。加密防了半天最后被自己的 console.log 卖了。建议拦截器里打日志时只打 HTTP 状态码、接口路径、耗时把响应体整个忽略。4.3 与 vue-router、Pinia 配合时的“最小加密”原则数据加密不是越多越好应该遵循最小原则。比如用户登录后token 和用户信息存在哪里。很多 Vue 项目习惯放 localStorage因为刷新页面不丢。但 localStorage 的 XSS 风险很高任何一个在页面里执行的第三方脚本都能把它读走。如果你的后端支持优先让后端把 token 放在HttpOnly的 Cookie 里前端完全接触不到自然也就不需要本地加密。如果必须用 localStorage 存至少用 AES 加密后再存虽然不能防 XSS但能防普通用户用 DevTools 直接复制。再比如 vue-router 的路由守卫。路由守卫只能控制页面跳转不能控制接口调用。你可以在router.beforeEach里判断用户有没有登录、有没有权限但真正数据是否泄露仍然取决于接口权限和请求参数是否加密、签名。不要把“路由守卫能进页面”等同于“数据安全”。Pinia 或 Vuex 里也一样不要为了取用方便把身份证号、密码字段常驻在内存里。内存虽然比 localStorage 安全但页面被嵌入 iframe、或者浏览器有恶意扩展时仍然存在被读取的风险。用完后尽快清掉或者只保留脱敏后的展示值。5. 踩坑记录这六种方式在真实项目里最容易翻车的地方再好的方案落地时都会遇到一些看起来很小、但能卡住你一整天的细节。这里把我踩过的坑集中整理一下每一条都是真实联调经历。5.1 中文乱码Base64 和 AES 都逃不过 UTF-8btoa(中文)在浏览器里会直接抛InvalidCharacterError这是 Base64 最经典的问题。很多人以为是 btoa 不支持中文其实是因为btoa期望输入是“0-255 范围”的二进制字符串而中文在 JS 字符串里以 UTF-16 存储。正确思路是先把中文编码成 UTF-8 字节再转 Base64也就是我在 3.1 里写的封装。AES 也一样。如果前端用CryptoJS.AES.encrypt(中文, key, ...)加密后端解密出来是中文没问题但如果前端在加密前自己做了escape、encodeURIComponent、或者后端解出来后用 ISO-8859-1 读取就会出现“解密成功但全是乱码”。我的建议是前后端联调的第一轮就用一个固定中文串“中文测试”加解密一遍能通过再继续调业务字段。不要用英文测试通过就觉得完事了中文问题只会延迟爆发不会消失。5.2 固定 IV相同的明文永远得到相同的密文固定密钥加上固定 IV会导致同一个明文的加密结果每次都一样。这在很多业务里不够安全因为攻击者可以通过比较密文判断“用户是不是修改了某个字段”。比如把“优惠金额”从 1 改成 2密文变没变、在哪个位置变了都可能暴露信息。反过来随机 IV 每次加密结果都不同后端照样能解前端代码也只需要多传一个 IV 字段。这个成本很低建议默认使用随机 IV。5.3 签名大小写、编码不统一MD5 和 HMAC 签名的最终输出十六进制字符串有些后端默认大写有些默认小写两边只要不一致联调三小时起。别问我怎么知道的。更隐蔽的是编码不统一。前端在拼签名字符串时用了encodeURIComponent后端如果用的是URLEncoder.encode它们在处理空格时结果会不一样encodeURIComponent( )输出%20Java 的URLEncoder.encode输出。解决办法是统一约定“签名源串里的特殊字符全部使用哪种编码”并在接口文档里写明。5.4 AES 模式和 padding 对不上AES 不是只有一种用法。它有好几种模式CBC、ECB、CTR、GCM还有好几种 paddingPKCS7、PKCS5、ZeroPadding、NoPadding。CryptoJS 默认的是 CBC Pkcs7Java 里常见的也是AES/CBC/PKCS5Padding两者正好能对上所以很多“默认配置”能跑通。但你要是碰到一个后端用 ECB 模式前端还按 CBC 传 IV那后端会直接报错“InvalidAlgorithmParameterException”。解决方式就是在接口文档里明确写清楚这一点算法AES模式CBC填充PKCS7密钥长度128 位IV 长度16 字节有了这段描述不管是 Java、Python、PHP、Go 还是 Node.js后端同事都能按同一套标准实现。5.5 RSA 公钥格式和加密长度JSEncrypt 默认接受的公钥是 PKCS#8 的 PEM 格式以-----BEGIN PUBLIC KEY-----开头。有些后端给的是 PKCS#1 格式以-----BEGIN RSA PUBLIC KEY-----开头JSEncrypt 也能认但如果你在复制过程中丢了换行、多了空格就会一直加密失败。除了格式长度限制也要在设计接口时就考虑。RSA 不适合加密长内容如果需求明确说“整个 JSON 报文都用 RSA 加密”我建议后端改成混合加密方案前端用 AES 加密业务数据用 RSA 加密 AES 密钥。这个方案在后端侧也很成熟谁都不想为了一个长文本把响应体拼成几百行的 RSA 密文。6. 我给不同项目开的“加密处方”前五部分是从方案出发这一部分我想换个角度从项目类型出发给一个可以直接抄的选型参考。6.1 纯内网后台管理系统Base64 MD5 已经够用如果你做的是企业内网管理系统用户量不大、网络环境相对可控那前端不需要把整个报文都搞成 AES 加密。登录密码做一次 MD5 加盐配合 HTTPS接口层面再做一个简单的 MD5 签名基本就够用了。这里“够用”的意思是你的核心风险已经不在传输链路而在权限管理和数据库审计上。6.2 面向公网的管理端/用户端HTTPS RSA/混合加密一旦系统暴露在公网密码、手机号这类敏感字段就不要再用 MD5 裸传了。登录密码用 RSA 加密用户中心展示类接口如果担心前端明文拿到太多可以后端用 AES 加密、前端解密展示。涉及长数据的走混合加密或者让后端直接返回脱敏数据而不是返回一个需要前端解密的完整明文字段。6.3 第三方开放接口HMAC 签名 时间戳 防重放如果你的 Vue 项目在扮演“开放平台前端”的角色要对接第三方接口最常见的认证方式是给每个请求加上 HMAC 签名。签名里必须带上timestamp和nonce后端校验时间戳是否在 5 分钟内并检查 nonce 是否用过。Vue 侧的代码就是我 3.4 里那段buildSignature只需要在 axios 请求拦截器里统一挂载即可。6.4 进阶选项Web Crypto API 和国密算法如果你不想引 crypto-js现代浏览器原生也支持 AES-GCM 加密。GCM 模式自带身份认证比 CBC 更安全代码也没想象中复杂export async function aesGcmEncrypt(plaintext, rawKey) { const cryptoKey await crypto.subtle.importKey( raw, rawKey, { name: AES-GCM }, false, [encrypt] ) const iv crypto.getRandomValues(new Uint8Array(12)) const encoded new TextEncoder().encode(plaintext) const encrypted await crypto.subtle.encrypt( { name: AES-GCM, iv }, cryptoKey, encoded ) const cipherBuffer new Uint8Array(iv.length encrypted.byteLength) cipherBuffer.set(iv, 0) cipherBuffer.set(new Uint8Array(encrypted), iv.length) return btoa(String.fromCharCode(...cipherBuffer)) }用的时候注意rawKey是 Uint8Array不能直接传字符串而且crypto.subtle只在 HTTPS 或 localhost 下可用。这种方式适合对包体积敏感、不想引入额外依赖的 Vue 3 Vite 项目。如果项目有国密合规要求后端可能会指定 SM2、SM3、SM4 这套算法。前端可以用sm-crypto这个库用法和 RSA、AES 很像。SM4 是对称加密密钥长度 16 字节SM2 是非对称加密有公钥和私钥SM3 是摘要输出 64 位十六进制。真正项目里是否要用以你的技术负责人和合规要求为准这里不展开。写了这么多最后说一句我的实际体会数据加密方案从来没有“最好”只有“最合适”。Vue 项目里最难的也不是代码怎么写而是想清楚数据在哪一段需要保密、在哪一段只需要防篡改、在哪一段根本不该出现。把明文可见范围尽量缩小把签名校验尽量前置把密钥管理尽量收敛这套思路比替换成任何一个“更安全的算法”都管用。
返回列表