ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

前端RSA加密实践:jsencrypt在uniapp中的适配与避坑指南

2026/9/7 12:18:17 拓冰建站 浏览量
前端RSA加密实践:jsencrypt在uniapp中的适配与避坑指南 简介这是一份针对UniApp和普通前端项目的RSA加密解密实战资源专门解决jsencrypt库在UniApp中引入时报错、无法编译通过的问题。资源包共3个文件包括已做好环境适配的jsencrypt.js、封装完成的rsa.js工具类以及记录使用步骤和公钥私钥生成的说明txt整体大小仅37KB下载后可直接放入项目的utils目录使用。其中jsencrypt.js已针对uni-app模块环境进行调整可避免常见的“JsEncrypt is not defined”等报错rsa.js导出了rsaEncrypt与rsaDecrypt两个方法开发者传入公钥或私钥即可完成加密解密说明文档还附带了在线生成公钥私钥的链接及配置要点方便快速接入登录密码、手机号等敏感信息的传输场景。该方案来自博主在真实项目中的排错总结从引入文件到封装调用环环相扣既解决了UniApp兼容性痛点又避免了重复造轮子。目前已有2803人学习下载适合需要快速集成RSA加密功能的前端开发者和uni-app跨端项目使用。 前端做RSA加密这个需求听着简单实际踩坑能踩出一整套笔记。我是在一个跨端项目里被逼着研究jsencrypt的那套代码不仅要跑在PC浏览器还得在uniapp打包的App和小程序里用同一个逻辑。折腾了一圈发现直接在uniapp里npm install jsencrypt然后import事情没那么顺利。如果你也在做登录密码加密、敏感字段脱敏传输或者正在为uniapp里的RSA加密发愁这篇文章能帮你省下不少试错时间。我会把原理、API、uniapp适配、前后端联调、以及真实项目里遇到的报错全部梳理清楚偏实操向照着改就能用。1. 先搞懂为什么前端要折腾RSA而不是直接传明文1.1 有HTTPS还不够业务层加密的适用场景很多人觉得网站都有HTTPS了前端再做一层加密纯属脱裤子放屁。这个观点对一半。HTTPS保护的是网络中传输的数据防止被第三方窃听和篡改但它保护不了后端日志、中间件、拦截器和一些内部的调试工具——数据到服务器以后绝大多数系统都会把请求参数打到日志里如果前端传的是明文密码、身份证号、手机号日志一旦泄露后果不用我多说。另一个现实场景是HTTP和HTTPS混用的内网系统或者走第三方网关转发的服务。在这种架构里链路不是你完全能控制的中间任何一层把报文记录下来都是明文。所以出于合规要求尤其是等保敏感字段必须在业务层就做端到端加密而不是依赖传输层的保护。前端用RSA加密后提交后端拿私钥解密公钥即使被泄露也解不开密文这就把数据安全和传输链路解耦了。1.2 公钥加密、私钥解密非对称加密到底怎么运转RSA是非对称加密算法核心特点是加密和解密使用不同的钥匙。公钥可以向任何人公开大家都可以用它来加密数据私钥必须由接收方严格保管只有私钥才能解开公钥加密的内容。这个机制天然适配前端场景服务端生成一对密钥把公钥交给前端前端用公钥加密用户输入的密码后端用私钥解密拿到明文。可以类比成快递柜公钥是柜门的投递口谁都可以往里塞东西私钥是柜员的取件钥匙只有柜员能打开柜门拿走包裹。就算投递口暴露在外面别人塞再多东西也拿不出来。在这个模型里私钥永远不会发送到前端所以即使前端代码被逆向攻击者也拿不到能解密数据的私钥。也正因为这一点网上很多“前端解密”的需求其实是不成立的因为私钥一旦出现在前端加密的意义就没了。2. jsencrypt引入与四个核心API实战2.1 安装引入npm方式与浏览器直接引入普通的前端项目Vue、React、原生JS用npm安装是最省事的npm install jsencrypt然后在组件里引入import JSEncrypt from jsencrypt如果你用的是原生HTML也可以直接在页面里引用jsencrypt的CDN文件暴露全局的JSEncrypt构造函数script srchttps://cdn.jsdelivr.net/npm/jsencrypt3.0.0/bin/jsencrypt.min.js/script script const encryptor new JSEncrypt() /scriptjsencrypt这个库封装了RSA底层的绝大多数脏活密钥格式解析、PKCS#1填充、Base64编解码都帮你处理好了。你不需要自己去管BigInteger运算、模幂运算这些数学细节只要传对密钥和方法名基本就能跑通。这也是为什么它一直是前端加密方案里的首选库。2.2 加密、解密、签名、验签四个方法jsencrypt的核心API其实就四个方法搞明白它们的配对关系就不会用错// 1. 加密用公钥加密明文 const encryptor new JSEncrypt() encryptor.setPublicKey(publicKey) const encrypted encryptor.encrypt(123456) // 2. 解密用私钥解密密文 const decryptor new JSEncrypt() decryptor.setPrivateKey(privateKey) const decrypted decryptor.decrypt(encrypted) // 3. 签名用私钥对内容签名用于身份确认 const signer new JSEncrypt() signer.setPrivateKey(privateKey) const signature signer.sign(content, CryptoJS.SHA256, sha256) // 4. 验签用公钥验证签名是否合法 const verifier new JSEncrypt() verifier.setPublicKey(publicKey) const result verifier.verify(content, signature, CryptoJS.SHA256)注意加密和解密是一对签名和验签是一对。前一对解决的是“数据泄露”问题后一对解决的是“数据被篡改”问题。签名时需要配合一个哈希函数常见的是配合crypto-js传入CryptoJS.SHA256如果你对签名源头比较看重也可以在加密方案里一起考虑国密算法但那要换库了。2.3 两个易踩的使用细节调用encrypt方法时如果密钥格式有问题或者明文长度超限它不会抛异常而是直接返回false或者空字符串。这一点非常坑——你以为是加密成功但结果不对实际上是压根没加密成功。所以在业务代码里最好加一层判断const encrypted encryptor.encrypt(password) if (!encrypted) { // 这里要处理异常不要继续提交请求 }另一个细节是setPublicKey传入的密钥字符串头和尾的-----BEGIN PUBLIC KEY-----、-----END PUBLIC KEY-----缺一不可换行符尽量保留。如果后端返回的是JSON格式经常会出现\r\n被转义的问题传进去就会报错。3. uniapp中用jsencrypt三种适配方案实测3.1 直接npm install后会遇到什么问题在uniapp项目里如果你只是简单执行npm install jsencrypt然后import大概率会遇到两类问题。第一类是编译期报错找不到window对象——因为jsencrypt源码内部引用了浏览器全局对象而微信小程序、App端的JS引擎里并没有window。第二类是运行时报错window.crypto或navigator为空导致的异常这在真机调试时尤其明显。我在一个企业微信小程序项目里第一次遇到这个报错时还很懵后来看了jsencrypt的源码才发现它在生成随机数时会调用window.crypto.getRandomValues。小程序环境里没有这个东西所以加密流程根本走不下去。解决思路就是对它做兼容让它在缺失的全局对象下也能工作。3.2 本地化改造方案从node_modules拷贝源码最稳妥的方案是绕开npm引用把jsencrypt源码拷贝到本地。操作步骤很简单安装完jsencrypt后在node_modules/jsencrypt/bin/下面找到jsencrypt.min.js把它复制到你项目的utils目录里然后直接相对引用。import JSEncrypt from /utils/jsencrypt.min.js如果遇到window is not defined可以在引入前做一层全局兼容处理。在入口文件比如main.js里加上// 兼容小程序环境防止 jsencrypt 找不到 window if (typeof window undefined) { global.window global }这里有一个坑要提醒在部分小程序平台上global对象不可用或者修改了global.window后子模块依然读取不到。我的经验是把这一行放到util模块的最顶部和jsencrypt的import保持在同一个文件里先声明再引入这样命中率最高。3.3 封装工具类H5和小程序通用为了不让代码里到处都是new JSEncrypt我习惯把它封装成一个工具模块。下面这个工具类我在uniapp项目里用过实测H5和小程序都能跑// utils/rsa.js import JSEncrypt from /utils/jsencrypt.min.js // 兼容处理 if (typeof window undefined) { global.window global } if (typeof window ! undefined !window.crypto) { window.crypto { getRandomValues: function (arr) { for (let i 0; i arr.length; i) { arr[i] Math.floor(Math.random() * 256) } return arr } } } export function rsaEncrypt(plaintext, publicKey) { const encryptor new JSEncrypt() encryptor.setPublicKey(publicKey) return encryptor.encrypt(plaintext) } export function rsaDecrypt(ciphertext, privateKey) { const decryptor new JSEncrypt() decryptor.setPrivateKey(privateKey) return decryptor.decrypt(ciphertext) }说句实在话window.crypto用一个基于Math.random的mock来替代在密码学上不够安全因为Math.random的随机性不足以抵御攻击。如果你的项目安全等级很高比如金融、支付类我建议别在前端做RSA运算把加密动作放到后端或云函数去处理。而在一般业务场景下这个工具类已经完全够用了。4. 完整实操生成密钥、前后端联调、分段加密4.1 用OpenSSL生成RSA密钥对先准备一对密钥来联调。本地用OpenSSL生成最方便openssl genrsa -out private.pem 2048 openssl rsa -in private.pem -pubout -out public.pem生成的private.pem是私钥文件public.pem是公钥文件。前端只需要公钥后端保留私钥。注意密钥长度选了2048位这是目前比较稳妥的底线——1024位已经不被安全标准推荐3072位属于安全余量更充足的选项但加密性能会明显下降尤其手机端会有感知。把公钥文件的内容完整复制到前端代码里这里要注意别丢失换行const publicKey -----BEGIN PUBLIC KEY----- MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC... -----END PUBLIC KEY-----4.2 前端加密、后端解密联调示例前端用封装好的工具类做加密import { rsaEncrypt } from /utils/rsa.js const encryptedPassword rsaEncrypt(userInputPassword, publicKey) // 这时候向后端提交的 password 字段就是一段 Base64 的密文后端拿到密文后用私钥解密。这里给一个Node.js后端的示例用node-rsa或node的内置crypto都行const NodeRSA require(node-rsa) const fs require(fs) const privateKey fs.readFileSync(./private.pem, utf8) const key new NodeRSA(privateKey) const decrypted key.decrypt(encryptedPassword, utf8)Java后端用Cipher.getInstance(RSA/ECB/PKCS1Padding)解密也是常见的重点是前后端必须约定好同一种填充方式。jsencrypt默认用的是RSA_PKCS1_PADDING后端也按这个来解就不会出问题。4.3 超长文本分段加密与混合加密方案RSA算法对单次加密的明文长度有硬性限制。以2048位密钥为例最大加密长度是2048/8 - 11 245字节超过这个长度encrypt方法会直接返回false。所以RSA不适合加密大段业务数据比如身份证照片、长文本备注这种。真实项目里遇到超长内容一般两种方案。第一是前端分段加密把明文按245字节切块逐块加密后拼接第二是混合加密用AES随机生成一个对称密钥加密业务数据再用RSA公钥加密这个AES密钥密文和数据一起传给后端。第二种方案性能更好也是HTTPS底层的思路。你只需要记住一句话触类旁通RSA负责加密“密钥”AES负责加密“正文”。给出一个简单的分段加密函数能在普通项目里应急用export function rsaLongEncrypt(plaintext, publicKey, chunkSize 245) { const encryptor new JSEncrypt() encryptor.setPublicKey(publicKey) const input encodeURIComponent(plaintext) const len input.length let result let offset 0 while (offset len) { const chunk input.substring(offset, offset chunkSize) const encryptedChunk encryptor.encrypt(chunk) if (!encryptedChunk) return null result encryptedChunk | offset chunkSize } return result }注意这里先做了encodeURIComponent因为RSA加密对中文字符容易出现编码不一致的问题先把字符串转成UTF-8形态的编码再分段能规避掉一部分乱码风险。4.4 公私钥签名验签怎么用签名验签的业务场景是“防篡改”和“防抵赖”。比如前端提交订单时参数里带一个签名串后端用公钥验证这个签名只要参数被改过签名就对不上。使用方式前面已经展示过sign方法需要配合哈希函数这里再补充一个常见用法前端用私钥签名适合App内下发私钥的场景后端用公钥验签。我要特别提醒一句签名用的私钥如果在移动端里被反编译拿到的风险很高。如果只是做登录态校验建议用对称加密比如HMAC方案即可非要上RSA签名要做好密钥保护方案比如放到服务端加签。5. 常见报错与经验避坑5.1 RSA public key not find到底是什么这个报错在jsencrypt的报错信息里曝光率极高。实际原因主要有四类公钥字符串里缺少了BEGIN或END标记、换行符在JSON传输中被吃掉、误把私钥传给了setPublicKey方法、或者后端返回的公钥做了URL编码导致格式错乱。我现在处理这个报错的经验是先打印公钥字符串肉眼检查有没有完整的-----BEGIN PUBLIC KEY-----头尾和换行。如果是从后端接口动态获取的公钥建议前端拿到后做一次清洗const cleanPublicKey publicKey.replace(/\\r\\n/g, \n).replace(/|/g, )这个处理解决了我至少八成的密钥格式问题。5.2 加密返回false、解密乱码等排查清单加密返回false第一检查明文长度是否超限第二检查密钥是否配对第三检查密钥格式。解密出来是乱码基本是前后端编码不一致导致的中文场景下尤其常见。解决方法是前端加密前先做encodeURIComponent后端解密后先做decodeURIComponent两边约好UTF-8编码问题就自然消失了。我整理了一个排查表格方便你在项目里快速定位症状可能原因解决方案加密返回false明文超长、密钥格式错分段加密、检查PEM完整报错public key not find密钥缺头尾、换行丢失清洗字符串、补全标记解密后乱码前后端编码不一致统一UTF-8加密前encodeURIComponent小程序端报window未定义环境缺少浏览器全局对象本地化引入jsencrypt并兼容window5.3 密钥位数选择与私钥暴露风险关于密钥长度我建议业务系统直接用2048位起步。1024位的密钥虽然解密快但现在已经有比较成熟的破解手段不适合用在涉及用户敏感信息的场景。3072位和4096位安全性更高但加密耗时成倍增长在低端安卓机上加密一个密码可能要几十毫秒体验明显变差。如果只是一般的登录密码加密2048位足够用如果是对安全性要求极高的金融数据建议把RSA运算放到后端统一处理。另外一个原则性的问题私钥永远不应该出现在前端代码里。前端只需要持有公钥解密动作全部交给后端。如果有人要求你在前端解密一定要问清楚这个需求的真实背景大概率是设计上存在隐患。前端开发要懂得对不合理的加密方案说不这也是保护用户数据安全的重要底线。我在实际项目里最终把这套封装固化成了团队工具库每次新项目直接引用后端联调时也统一了密钥格式的约定——公钥必须包含首尾标记、换行保留、JSON传输时不做URL编码。这几个约定写进接口文档后因为密钥格式导致的联调冲突基本归零。如果你也在做类似方案建议把第5.1节的清洗逻辑和后端的密钥生成规则一起定好这是前端做RSA加密最容易被忽略、也最容易返工的一个环节。本文还有配套的精品资源点击获取