Vue 登录密码为什么要 RSA 加密?一文讲透前后端实现

Vue 登录密码为什么要 RSA 加密?一文讲透前后端实现 Vue 前端 RSA 密码加密登录从原理到落地标签Vue · 前端安全 · RSA · JSEncrypt · 登录鉴权更新时间2026-06-29前言做 Web 登录时一个常见问题是密码能不能明文传给后端答案当然是不行。即使用了 HTTPS在浏览器到服务端这段链路里仍有不少场景可能暴露敏感信息——抓包调试、日志打印、中间代理、XSS 脚本读取表单等。因此很多团队会在前端对密码做RSA 非对称加密浏览器只持有公钥服务端持有私钥密码以密文形式传输降低泄露风险。本文不绑定任何具体业务项目只讲一套Vue JSEncrypt RSA的通用登录加密方案可直接用于博客分享或新项目落地。一、为什么选 RSA而不是自己「发明」加密先明确一件事RSA 不是用来替代 HTTPS 的而是 HTTPS 之上的一层业务防护。方案优点缺点明文传输实现简单风险最高前端 MD5/SHA看起来「加密了」可被重放且哈希值本身可当密码用AES 对称加密速度快密钥放前端等于公开RSA 非对称加密公钥可公开私钥只在服务端密文较长不适合大数据典型分工前端拿公钥把用户输入的密码加密成 Base64 密文后端拿私钥解密得到明文后再做 BCrypt / Argon2 等哈希比对这就是「锁和钥匙分离」公钥相当于锁谁都能锁私钥相当于钥匙只有服务端能开。二、整体流程一张图看懂后端服务请求层前端页面用户后端服务请求层前端页面用户输入账号 密码表单校验RSA 公钥加密密码提交登录参数POST /api/auth/loginRSA 私钥解密校验密码 签发 Token返回 Token 与用户信息持久化登录态登录成功跳转首页一句话总结用户看到的是明文密码网络上传的是密文数据库里存的是哈希。三、技术选型层级推荐方案前端框架Vue 2 / Vue 3 均可HTTPAxios / Fetch加密库JSEncrypt算法RSA-2048PKCS#1 v1.5 填充公钥存放环境变量或独立配置文件私钥存放仅服务端不进仓库、不进前端安装依赖npminstalljsencrypt四、前端实现通用写法4.1 配置公钥公钥建议放在环境变量中便于不同环境切换# .env.development / .env.production VUE_APP_RSA_PUBLIC_KEY你的RSA公钥Base64字符串公钥可以公开私钥绝不能出现在前端代码或环境变量里。4.2 封装加密工具函数importJSEncryptfromjsencrypt/** * 使用 RSA 公钥加密明文 * param {string} plainText - 待加密字符串如密码 * returns {string|null} Base64 密文失败返回 null */exportfunctionrsaEncrypt(plainText){constpublicKeyprocess.env.VUE_APP_RSA_PUBLIC_KEYif(!publicKey||!plainText)returnnullconstencryptornewJSEncrypt()encryptor.setPublicKey(publicKey)returnencryptor.encrypt(plainText)}4.3 登录页调用示例以下是完全独立的示例代码展示「校验 → 加密 → 请求 → 存 Token」的标准写法import{rsaEncrypt}from/utils/rsaEncryptimport{loginApi}from/api/authexportdefault{data(){return{form:{username:,password:},loading:false}},methods:{asynchandleLogin(){if(!this.form.username||!this.form.password){this.$message.warning(请输入账号和密码)return}constencryptedPwdrsaEncrypt(this.form.password)if(!encryptedPwd){this.$message.error(密码加密失败请稍后重试)return}this.loadingtruetry{constresawaitloginApi({username:this.form.username,password:encryptedPwd})// 保存 Token跳转首页localStorage.setItem(token,res.data.token)this.$router.push(/)}finally{this.loadingfalse}}}}4.4 请求体长什么样假设用户输入密码123456前端发出的 JSON 大致如下{username:demo_user,password:pY8Ve/JioBQFDjFNzpHHREHoWp9s1CdZ/MixYlBb...}其中password字段是RSA 加密后的 Base64 字符串不是明文。4.5 密文示例说明明文123456项目说明明文123456算法RSA-2048 / PKCS#1 v1.5输出Base64 字符串一个重要细节RSA 使用 PKCS#1 填充时同一明文每次加密结果可能不同填充含随机数但后端解密后都能得到相同的123456。所以 Postman 调试时不要复制旧密文长期使用应每次重新加密。本地验证加密是否正常可用 Node.js 内置crypto模块constcryptorequire(crypto)// 将 Base64 公钥转为 PEM 格式functiontoPem(base64Key){constlinesbase64Key.match(/.{1,64}/g).join(\n)return-----BEGIN PUBLIC KEY-----\n${lines}\n-----END PUBLIC KEY-----}constpublicKeyBase64你的公钥Base64字符串constpemtoPem(publicKeyBase64)constciphercrypto.publicEncrypt({key:pem,padding:crypto.constants.RSA_PKCS1_PADDING},Buffer.from(123456,utf8))console.log(cipher.toString(base64))五、后端如何解密后端收到密文后用RSA 私钥解密得到明文密码再进入常规鉴权流程。5.1 JavaSpring Bootimportjavax.crypto.Cipher;importjava.nio.charset.StandardCharsets;importjava.security.KeyFactory;importjava.security.PrivateKey;importjava.security.spec.PKCS8EncodedKeySpec;importjava.util.Base64;publicclassRsaDecryptUtil{// 私钥仅保存在服务端配置中心或密钥管理服务中privatestaticfinalStringPRIVATE_KEY_BASE64你的私钥Base64字符串;publicstaticStringdecrypt(StringcipherTextBase64)throwsException{byte[]keyBytesBase64.getDecoder().decode(PRIVATE_KEY_BASE64);PKCS8EncodedKeySpecspecnewPKCS8EncodedKeySpec(keyBytes);PrivateKeyprivateKeyKeyFactory.getInstance(RSA).generatePrivate(spec);CiphercipherCipher.getInstance(RSA/ECB/PKCS1Padding);cipher.init(Cipher.DECRYPT_MODE,privateKey);byte[]plainBytescipher.doFinal(Base64.getDecoder().decode(cipherTextBase64));returnnewString(plainBytes,StandardCharsets.UTF_8);}}Controller 中的典型用法PostMapping(/api/auth/login)publicApiResultlogin(RequestBodyLoginRequestreq)throwsException{StringplainPasswordRsaDecryptUtil.decrypt(req.getPassword());// plainPassword 此时为 123456再与数据库哈希比对returnauthService.login(req.getUsername(),plainPassword);}5.2 C#.NET 6usingSystem.Security.Cryptography;usingSystem.Text;publicstaticclassRsaHelper{privateconststringPrivateKeyBase64你的私钥Base64字符串;publicstaticstringDecrypt(stringcipherTextBase64){varrsaRSA.Create();rsa.ImportPkcs8PrivateKey(Convert.FromBase64String(PrivateKeyBase64),out_);varcipherBytesConvert.FromBase64String(cipherTextBase64);varplainBytesrsa.Decrypt(cipherBytes,RSAEncryptionPadding.Pkcs1);returnEncoding.UTF8.GetString(plainBytes);// 例如 123456}}5.3 后端加密接口测试 / 第三方对接若需要在 Postman 或脚本里模拟前端加密stringRsaEncrypt(stringplainText,stringpublicKeyBase64){varrsaRSA.Create();rsa.ImportSubjectPublicKeyInfo(Convert.FromBase64String(publicKeyBase64),out_);varbytesrsa.Encrypt(Encoding.UTF8.GetBytes(plainText),RSAEncryptionPadding.Pkcs1);returnConvert.ToBase64String(bytes);}varencryptedRsaEncrypt(123456,publicKey);六、密钥怎么生成开发阶段可用 OpenSSL 快速生成一对 2048 位密钥# 生成私钥openssl genrsa-outprivate.pem2048# 从私钥导出公钥openssl rsa-inprivate.pem-pubout-outpublic.pem# 转为 PKCS#8 格式Java 常用openssl pkcs8-topk8-informPEM-outformPEM-nocrypt-inprivate.pem-outprivate_pkcs8.pem公钥→ 配置到前端环境变量私钥→ 配置到后端权限最小化禁止提交 Git七、还有哪些场景可以用同一套方案只要涉及「敏感字符串从前端传到后端」都可以复用同一公钥加密逻辑场景加密字段用户登录password修改密码oldPassword、newPassword、confirmPassword重置密码newPassword第三方凭证绑定secret、token 等短字符串原则短文本、低频传输适合 RSA大段数据应改用 AES RSA 混合加密或纯 HTTPS 传输。八、踩坑清单血泪经验1. 前后端填充方式不一致JSEncrypt 默认 PKCS#1 v1.5Java 需用RSA/ECB/PKCS1Padding.NET 需用RSAEncryptionPadding.Pkcs1。填错一个字母解密就是乱码。2. 公钥私钥不是一对前端换了公钥后端没换私钥或者开发/生产环境密钥混用登录永远失败。建议密钥按环境隔离管理。3. 以为密文可以复用RSA 密文带随机填充不能把 A 时刻的密文当作 B 时刻的密码用。自动化测试要每次动态加密。4. 把 RSA 当万能盾RSA 防不了 XSS脚本仍能读输入框明文RSA 防不了重放需配合 nonce、时间戳、HTTPSRSA 替代不了密码哈希存储正确姿势传输加密RSA 存储哈希BCrypt 传输通道HTTPS三层各司其职。5. 私钥泄露私钥一旦进 Git 历史等于所有历史密文都可被离线解密。务必用.gitignore、密钥管理服务、CI 注入等方式隔离。九、落地 Checklist对接时可按此清单逐项核对生成 RSA-2048 密钥对公钥给前端私钥给后端前端安装jsencrypt封装rsaEncrypt工具函数登录接口传参前对密码字段加密后端登录接口先解密再校验哈希前后端统一 PKCS#1 v1.5 填充生产环境启用 HTTPS私钥不进仓库、不进前端Postman / 自动化测试支持动态加密十、总结Vue 登录密码 RSA 加密并不复杂核心就四步前端用公钥把密码锁起来网络上传输的是 Base64 密文后端用私钥打开得到明文数据库只存哈希不存明文以123456为例用户输入的是123456线上飞的是一长串 Base64后端解出来还是123456最后和库里 BCrypt 哈希比对——各司其职清晰可控。如果你正在设计登录模块希望这篇能帮你少踩几个坑。欢迎收藏、转发也欢迎在评论区交流你的实践经验。延伸阅读JSEncrypt 官方仓库RFC 8017 - PKCS #1OWASP 密码存储备忘单