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 等哈希比对
这就是「锁和钥匙分离」:公钥相当于锁,谁都能锁;私钥相当于钥匙,只有服务端能开。
二、整体流程(一张图看懂)
一句话总结:用户看到的是明文密码,网络上传的是密文,数据库里存的是哈希。
三、技术选型
| 层级 | 推荐方案 |
|---|---|
| 前端框架 | Vue 2 / Vue 3 均可 |
| HTTP | Axios / Fetch |
| 加密库 | JSEncrypt |
| 算法 | RSA-2048,PKCS#1 v1.5 填充 |
| 公钥存放 | 环境变量或独立配置文件 |
| 私钥存放 | 仅服务端,不进仓库、不进前端 |
安装依赖:
npminstalljsencrypt四、前端实现(通用写法)
4.1 配置公钥
公钥建议放在环境变量中,便于不同环境切换:
# .env.development / .env.production VUE_APP_RSA_PUBLIC_KEY=你的RSA公钥Base64字符串公钥可以公开;私钥绝不能出现在前端代码或环境变量里。
4.2 封装加密工具函数
importJSEncryptfrom'jsencrypt'/** * 使用 RSA 公钥加密明文 * @param {string} plainText - 待加密字符串(如密码) * @returns {string|null} Base64 密文,失败返回 null */exportfunctionrsaEncrypt(plainText){constpublicKey=process.env.VUE_APP_RSA_PUBLIC_KEYif(!publicKey||!plainText)returnnullconstencryptor=newJSEncrypt()encryptor.setPublicKey(publicKey)returnencryptor.encrypt(plainText)}4.3 登录页调用示例
以下是完全独立的示例代码,展示「校验 → 加密 → 请求 → 存 Token」的标准写法:
import{rsaEncrypt}from'@/utils/rsaEncrypt'import{loginApi}from'@/api/auth'exportdefault{data(){return{form:{username:'',password:''},loading:false}},methods:{asynchandleLogin(){if(!this.form.username||!this.form.password){this.$message.warning('请输入账号和密码')return}constencryptedPwd=rsaEncrypt(this.form.password)if(!encryptedPwd){this.$message.error('密码加密失败,请稍后重试')return}this.loading=truetry{constres=awaitloginApi({username:this.form.username,password:encryptedPwd})// 保存 Token,跳转首页localStorage.setItem('token',res.data.token)this.$router.push('/')}finally{this.loading=false}}}}4.4 请求体长什么样?
假设用户输入密码123456,前端发出的 JSON 大致如下:
{"username":"demo_user","password":"pY8Ve/JioBQFDjF++NzpHHREHoWp9s1CdZ/MixYlBb+..."}其中password字段是RSA 加密后的 Base64 字符串,不是明文。
4.5 密文示例说明(明文:123456)
| 项目 | 说明 |
|---|---|
| 明文 | 123456 |
| 算法 | RSA-2048 / PKCS#1 v1.5 |
| 输出 | Base64 字符串 |
一个重要细节:RSA 使用 PKCS#1 填充时,同一明文每次加密结果可能不同(填充含随机数),但后端解密后都能得到相同的123456。所以 Postman 调试时,不要复制旧密文长期使用,应每次重新加密。
本地验证加密是否正常,可用 Node.js 内置crypto模块:
constcrypto=require('crypto')// 将 Base64 公钥转为 PEM 格式functiontoPem(base64Key){constlines=base64Key.match(/.{1,64}/g).join('\n')return`-----BEGIN PUBLIC KEY-----\n${lines}\n-----END PUBLIC KEY-----`}constpublicKeyBase64='你的公钥Base64字符串'constpem=toPem(publicKeyBase64)constcipher=crypto.publicEncrypt({key:pem,padding:crypto.constants.RSA_PKCS1_PADDING},Buffer.from('123456','utf8'))console.log(cipher.toString('base64'))五、后端如何解密?
后端收到密文后,用RSA 私钥解密,得到明文密码,再进入常规鉴权流程。
5.1 Java(Spring Boot)
importjavax.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[]keyBytes=Base64.getDecoder().decode(PRIVATE_KEY_BASE64);PKCS8EncodedKeySpecspec=newPKCS8EncodedKeySpec(keyBytes);PrivateKeyprivateKey=KeyFactory.getInstance("RSA").generatePrivate(spec);Ciphercipher=Cipher.getInstance("RSA/ECB/PKCS1Padding");cipher.init(Cipher.DECRYPT_MODE,privateKey);byte[]plainBytes=cipher.doFinal(Base64.getDecoder().decode(cipherTextBase64));returnnewString(plainBytes,StandardCharsets.UTF_8);}}Controller 中的典型用法:
@PostMapping("/api/auth/login")publicApiResultlogin(@RequestBodyLoginRequestreq)throwsException{StringplainPassword=RsaDecryptUtil.decrypt(req.getPassword());// plainPassword 此时为 "123456",再与数据库哈希比对returnauthService.login(req.getUsername(),plainPassword);}5.2 C#(.NET 6+)
usingSystem.Security.Cryptography;usingSystem.Text;publicstaticclassRsaHelper{privateconststringPrivateKeyBase64="你的私钥Base64字符串";publicstaticstringDecrypt(stringcipherTextBase64){varrsa=RSA.Create();rsa.ImportPkcs8PrivateKey(Convert.FromBase64String(PrivateKeyBase64),out_);varcipherBytes=Convert.FromBase64String(cipherTextBase64);varplainBytes=rsa.Decrypt(cipherBytes,RSAEncryptionPadding.Pkcs1);returnEncoding.UTF8.GetString(plainBytes);// 例如 "123456"}}5.3 后端加密(接口测试 / 第三方对接)
若需要在 Postman 或脚本里模拟前端加密:
stringRsaEncrypt(stringplainText,stringpublicKeyBase64){varrsa=RSA.Create();rsa.ImportSubjectPublicKeyInfo(Convert.FromBase64String(publicKeyBase64),out_);varbytes=rsa.Encrypt(Encoding.UTF8.GetBytes(plainText),RSAEncryptionPadding.Pkcs1);returnConvert.ToBase64String(bytes);}varencrypted=RsaEncrypt("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.5,Java 需用RSA/ECB/PKCS1Padding,.NET 需用RSAEncryptionPadding.Pkcs1。填错一个字母,解密就是乱码。
2. 公钥私钥不是一对
前端换了公钥,后端没换私钥,或者开发/生产环境密钥混用,登录永远失败。建议密钥按环境隔离管理。
3. 以为密文可以复用
RSA 密文带随机填充,不能把 A 时刻的密文当作 B 时刻的密码用。自动化测试要每次动态加密。
4. 把 RSA 当万能盾
- RSA 防不了 XSS(脚本仍能读输入框明文)
- RSA 防不了重放(需配合 nonce、时间戳、HTTPS)
- RSA 替代不了密码哈希存储
正确姿势:传输加密(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 #1
- OWASP 密码存储备忘单
