目录
- 一、对称加密、非对称加密、杂凑算法(哈希算法)、消息认证码
-
- [1、 对称加密](#1、 对称加密)
- 2、非对称加密
- 3、杂凑算法(哈希算法)
- [4、 三者的核心区别](#4、 三者的核心区别)
- [5、 联系与协同应用](#5、 联系与协同应用)
- 6、拓展
- [二、 常见加密算法对比](#二、 常见加密算法对比)
- 三、RSA使用
- [四、AES 使用](#四、AES 使用)
- 五、SM3使用
- 六、SHA-256使用
- 七、HMAC-SHA256使用
- 八、MD5使用
- 九、DES使用
- 十、各库核心功能与引入方式
- 十一、补充知识点
-
- CryptoJS加密库常用方法详解
- [js-base64 库常用方法详解](#js-base64 库常用方法详解)
- URL编码解码详解:escape、encodeURI、encodeURIComponent
一、对称加密、非对称加密、杂凑算法(哈希算法)、消息认证码
1、 对称加密
对称加密使用单一密钥进行加密和解密,算法效率高,适合处理大量数据。密钥必须通过安全渠道共享,若密钥泄露则加密失效。典型算法包括AES(高级加密标准)、DES(数据加密标准)和3DES。
典型应用场景:
- 文件或数据库加密
- 网络通信的批量数据加密(如TLS协议中的会话密钥)
2、非对称加密
非对称加密使用一对密钥(公钥和私钥),公钥可公开分发,私钥严格保密。加密速度较慢,但解决了密钥分发问题。典型算法包括RSA(基于大数分解)和ECC(椭圆曲线加密)。
典型应用场景:
- 安全密钥交换(如Diffie-Hellman)
- 数字签名(如SSL证书验证)
- 混合加密体系(如TLS中非对称加密交换对称密钥)
3、杂凑算法(哈希算法)
单向加密,生成固定长度摘要,无法逆向还原。用于数据完整性验证。典型算法包括SM3、MD5、SHA-256。
典型场景:密码存储、文件校验。
4、 三者的核心区别
密钥机制:
- 对称加密:1个共享密钥
- 非对称加密:公钥+私钥
- 杂凑算法:无密钥(MD5/SHA)或带密钥(HMAC)
性能与用途:
-
对称加密:高速,适合数据加密
-
非对称加密:低速,适合密钥管理和身份验证
-
杂凑算法:快速数据指纹生成
安全性依赖:
- 对称加密:密钥传输安全性
- 非对称加密:私钥保密性
- 杂凑算法:抗碰撞能力(如SM3优于MD5)
5、 联系与协同应用
- 混合加密系统:非对称加密分发对称密钥(如TLS协议),后续通信使用对称加密。
- 完整性验证:HMAC可与对称/非对称加密结合,确保数据未被篡改(如加密文件附加HMAC)。
- 数字签名:非对称加密的私钥生成签名,公钥验证,而杂凑算法(如SHA-256)压缩原始数据提升签名效率。
- 密码存储:用户密码经SM3哈希后存储,登录时对比哈希值,避免明文泄露风险。
- 技术互补性 :
- 对称加密解决效率问题,非对称加密解决信任问题,HMAC解决篡改问题。
- 现代协议(如TLS/SSL)通常三者结合使用以实现全面安全。
6、拓展
6.1 HMAC-SHA256
- HMAC-SHA256属于消息认证码(MAC),通过密钥和哈希函数(SHA-256)生成消息摘要,用于验证数据完整性和真实性。它不是对称加密也不是非对称加密,而是独立的安全机制。
- 典型应用场景:
- API请求认证(如JWT签名)
- 防止数据篡改(如文件传输校验)
6.2 HMAC-SHA256与SHA256的核心区别
-
定义差异
SHA256是单向哈希函数,将任意输入转换为固定256位哈希值,用于数据完整性校验。HMAC-SHA256是消息认证码算法,通过结合SHA256和密钥实现消息认证与完整性保护。
-
依赖密钥
SHA256不依赖密钥,相同输入必然产生相同输出。HMAC-SHA256必须使用密钥,即使输入相同,不同密钥也会生成不同结果。
-
关键技术特性对比
-
安全性
SHA256仅验证数据未被篡改。HMAC-SHA256额外提供身份验证,防止伪造,且能抵抗长度扩展攻击。
-
计算过程
SHA256执行单次哈希计算。HMAC-SHA256需两次哈希计算(密钥与输入混合处理),复杂度更高但安全性更强。
-
输出一致性
SHA256的输出仅取决于输入数据。HMAC-SHA256的输出由输入数据和密钥共同决定。
-
-
典型应用场景
-
SHA256适用场景
文件校验(如下载文件完整性检查)、密码存储(加盐哈希)、区块链(加密货币交易验证)、数字签名(非对称加密配套使用)。
-
HMAC-SHA256适用场景
API安全(请求签名防篡改)、网络协议(如TLS/SSL部分实现)、敏感数据传输(验证消息来源真实性)。
-
二、 常见加密算法对比
-
RSA
非对称加密算法,使用公钥和私钥配对。加密速度较慢,适用于小数据加密或密钥交换场景,如SSL/TLS握手阶段。
-
AES
对称加密算法,依赖共享密钥。加密速度快,适合大规模数据加密,如文件存储、数据库字段加密或通信信道加密。
-
DES
对称加密算法,同样需要共享密钥。速度中等,因安全性不足(56位密钥)已逐渐被淘汰,仅用于兼容遗留系统。
-
MD5
生成128位哈希值,但已被发现存在严重的碰撞漏洞,安全性很低。不推荐用于任何安全敏感场景。仅在部分非安全的文件完整性校验等遗留场景中可见。
-
SM3
中国国家密码管理局发布的密码杂凑算法,输出256位哈希值,设计目标包括抗碰撞性和单向性,其功能与国际上的SHA-256类似。适用于数字签名、消息认证码生成、随机数生成等场景,常与SM2、SM4构成国密算法体系。
-
SHA-256
属于SHA-2家族,生成256位哈希值,具备较高的抗碰撞性。广泛应用于需要高安全性哈希的场景,如区块链(比特币)、TLS证书等。
-
HMAC-SHA256
基于SHA-256哈希算法和预共享密钥的消息认证码算法,用于验证消息的完整性和真实性。主要用于API请求签名验证、防篡改、防重放攻击等场景。
- 特点与应用建议
- 追求安全性与性能平衡:对于大量数据加密,优先使用AES对称加密算法。
- 当需要安全地交换密钥或进行数字签名时,再引入RSA非对称加密算法。
- 确保数据完整性与真实性:在网络API通信等场景中,使用HMAC-SHA256来提供消息认证,防止数据在传输过程中被篡改或伪造。
- 处理历史系统:对于仍在使用DES或MD5的系统,应视其为安全风险,制定计划迁移至AES和SHA-256/SM3
- 考虑合规性:在国内的商用密码应用场景中,需遵循国家密码管理局的要求,考虑使用国密算法体系(如SM3)
- 注:实际选择需结合具体需求,如合规性、硬件支持等。
三、RSA使用
JSEncrypt基于RSA算法,通过公钥加密、私钥解密实现安全通信。公钥可公开分发,私钥需严格保密,加密数据仅能通过配对私钥解密.
1、安装依赖与基本使用
pnpm install jsencrypt
或者
npm install jsencrypt
基本封装
// src/utils/rsa.js
import JSEncrypt from 'jsencrypt'
/**
* RSA 加密工具类
* 适用于数据量较小的加密场景
*/
class RSAUtil {
constructor() {
this.encryptor = new JSEncrypt()
this.privateKey = '' // 私钥
this.publicKey = '' // 公钥
}
/**
* 设置公钥
* @param {string} publicKey - PEM 格式的公钥
*/
setPublicKey(publicKey) {
this.publicKey = publicKey
this.encryptor.setPublicKey(publicKey)
}
/**
* 设置私钥
* @param {string} privateKey - PEM 格式的私钥
*/
setPrivateKey(privateKey) {
this.privateKey = privateKey
this.encryptor.setPrivateKey(privateKey)
}
/**
* 使用公钥加密数据
* @param {string} data - 要加密的明文数据
* @returns {string} Base64 编码的加密结果
*/
encrypt(data) {
if (!this.publicKey) {
throw new Error('请先设置公钥')
}
try {
// JSEncrypt 会自动处理 Base64 编码
const encrypted = this.encryptor.encrypt(data)
if (!encrypted) {
throw new Error('加密失败,可能是数据过长或密钥不匹配')
}
return encrypted
} catch (error) {
console.error('加密过程出错:', error)
throw new Error(`加密失败: ${error.message}`)
}
}
/**
* 使用私钥解密数据
* @param {string} encryptedData - Base64 编码的加密数据
* @returns {string} 解密后的明文数据
*/
decrypt(encryptedData) {
if (!this.privateKey) {
throw new Error('请先设置私钥')
}
try {
const decrypted = this.encryptor.decrypt(encryptedData)
if (!decrypted) {
throw new Error('解密失败,可能是数据损坏或密钥不匹配')
}
return decrypted
} catch (error) {
console.error('解密过程出错:', error)
throw new Error(`解密失败: ${error.message}`)
}
}
}
// 导出单例实例
export default new RSAUtil()
使用示例
//引入
import RSAUtil from '@/utils/rsa'
//先设置公钥秘钥
RSAUtil.setPublicKey('公钥')
RSAUtil.setPrivateKey('私钥')
//加密
RSAUtil.encrypt('待加密数据')
//解密
RSAUtil.decrypt('待解密数据')
RSA分段加密工具类
// src/utils/rsa-segment.js
import JSEncrypt from 'jsencrypt'
/**
* RSA分段加密工具类
* 解决RSA加密数据长度限制问题
* 支持2048位密钥,最大明文长度245字节
*
* 为什么需要分段加密?
* RSA算法对加密数据的长度有严格限制:
* - 使用2048位密钥时,最大能加密245字节的明文
* - 如果数据超过这个长度,必须分割成多个片段分别加密
*
* 分段加密的工作流程:
* 1. 检查数据长度是否需要分段
* 2. 如果需要分段,将数据按字节长度分割成多个片段
* 3. 对每个片段分别进行RSA加密
* 4. 将加密结果组合成一个字符串,包含分段信息和分隔符
* 5. 解密时反向操作:解析分段信息,分别解密每个片段,最后合并
*/
class RSASegmentUtil {
constructor() {
// 创建JSEncrypt实例,用于执行RSA加密解密操作
this.encryptor = new JSEncrypt()
// 私钥:用于解密数据,必须严格保密
this.privateKey = ''
// 公钥:用于加密数据,可以公开
this.publicKey = ''
/**
* RSA 2048位密钥的加密长度限制计算:
* - RSA密钥长度:2048位 = 256字节
* - PKCS#1 v1.5填充方案:占用11字节(用于安全性和兼容性)
* - 最大明文长度 = 256字节 - 11字节 = 245字节
*
* 重要:这里的245字节是指明文的UTF-8编码字节数,不是字符数!
* 一个中文字符通常占用3个字节,所以最多只能加密约81个中文字符(245 ÷ 3 ≈ 81)
*/
this.MAX_ENCRYPT_LENGTH = 245
/**
* Base64编码后的密文最大长度计算:
* - RSA加密后生成256字节的二进制数据
* - Base64编码规则:每3个字节编码为4个字符
* - 256字节 ÷ 3 = 85组余1字节
* - Base64长度 = 85 × 4 + 4 = 344字符
*
* 这个长度信息主要用于调试和验证
*/
this.ENCRYPTED_SEGMENT_LENGTH = 344
/**
* 分段分隔符:
* - 用于分隔加密后的各个片段
* - 选择"::SEG::"作为分隔符,因为:
* 1. Base64字符集中不包含":"和"::"
* 2. 足够独特,不会意外出现在密文中
* 3. 易于识别和解析
*
* 为什么不使用单个字符作为分隔符?
* 因为Base64密文中可能包含各种字符,使用独特的多字符分隔符更安全
*/
this.SEGMENT_DELIMITER = '::SEG::'
}
/**
* 设置公钥
* @param {string} publicKey - PEM格式的公钥字符串
*
* PEM格式示例:
* -----BEGIN PUBLIC KEY-----
* MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...
* -----END PUBLIC KEY-----
*
* 注意:公钥必须在使用加密功能前设置
*/
setPublicKey(publicKey) {
this.publicKey = publicKey
this.encryptor.setPublicKey(publicKey)
}
/**
* 设置私钥
* @param {string} privateKey - PEM格式的私钥字符串
*
* PEM格式示例:
* -----BEGIN PRIVATE KEY-----
* MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQ...
* -----END PRIVATE KEY-----
*
* 注意:私钥必须在使用解密功能前设置
*/
setPrivateKey(privateKey) {
this.privateKey = privateKey
this.encryptor.setPrivateKey(privateKey)
}
/**
* 计算字符串的UTF-8字节长度
* @param {string} str - 输入字符串
* @returns {number} 字符串的UTF-8编码字节数
*
* 为什么需要这个方法?
* - JavaScript的String.length返回的是字符数,不是字节数
* - 中文字符:1个字符 = 3字节(UTF-8编码)
* - 英文字符:1个字符 = 1字节
* - RSA加密限制针对的是字节数,不是字符数
*/
getStringByteLength(str) {
// 方法1:使用现代浏览器API(推荐)
if (typeof TextEncoder !== 'undefined') {
// TextEncoder.encode()将字符串转换为UTF-8字节数组
// 返回的Uint8Array的length就是字节数
return new TextEncoder().encode(str).length
}
// 方法2:兼容方案,适用于不支持TextEncoder的环境
// encodeURIComponent会将非ASCII字符转换为%XX格式
// 例如:"你" → "%E4%BD%A0"(3个字节)
// 正则表达式/%[A-F\d]{2}/g匹配所有%XX格式的编码
// 替换为"U"(1个字符),然后计算长度
return encodeURIComponent(str).replace(/%[A-F\d]{2}/g, 'U').length
}
/**
* 将字符串按字节长度分割为多个片段
* @param {string} str - 输入字符串
* @param {number} maxByteLength - 每个片段的最大字节长度
* @returns {Array<string>} 分割后的字符串数组
*
* 分段算法说明:
* 1. 遍历字符串的每个字符
* 2. 估算每个字符的字节长度
* 3. 累加字节长度,当超过最大限制时开始新片段
* 4. 注意:不能从字符中间断开,必须按字符边界分割
*/
splitStringByByteLength(str, maxByteLength) {
const segments = [] // 存储分割后的片段
let currentSegment = '' // 当前正在构建的片段
let currentByteLength = 0 // 当前片段的字节长度
// 遍历字符串的每个字符
for (let i = 0; i < str.length; i++) {
const char = str[i] // 当前字符
let charByteLength // 当前字符的字节长度
// 根据Unicode码点估算字符的字节长度
// UTF-8编码规则:
// - 0-127: 1字节 (ASCII字符)
// - 128-2047: 2字节 (大部分拉丁字母扩展)
// - 2048-65535: 3字节 (大部分中文、日文、韩文)
// - 65536以上: 4字节 (表情符号、生僻字)
const charCode = char.charCodeAt(0)
if (charCode < 128) {
// ASCII字符:英文字母、数字、标点符号
charByteLength = 1
} else if (charCode < 2048) {
// 大部分拉丁字母扩展字符:2字节
charByteLength = 2
} else if (charCode < 65536) {
// 大部分中文、日文、韩文字符:3字节
charByteLength = 3
} else {
// 表情符号、生僻字:4字节
charByteLength = 4
}
/**
* 判断是否需要开始新片段:
* 条件1:当前片段加上这个字符会超过最大字节限制
* 条件2:当前片段不为空(如果为空,说明这个字符本身就超过了限制,那也没办法)
*/
if (currentByteLength + charByteLength > maxByteLength && currentSegment !== '') {
// 保存当前片段,开始新片段
segments.push(currentSegment)
currentSegment = char
currentByteLength = charByteLength
} else {
// 继续添加到当前片段
currentSegment += char
currentByteLength += charByteLength
}
}
// 处理最后一个片段
if (currentSegment !== '') {
segments.push(currentSegment)
}
return segments
}
/**
* 分段加密(自动处理长文本)
* @param {string} data - 要加密的明文数据
* @returns {string} 加密后的数据(包含分段信息)
*
* 返回值格式:
* - 不分段:"0::SEG::Base64密文"
* - 分段:"N::SEG::密文1::SEG::密文2::SEG::..." (N为段数)
*/
encryptLong(data) {
// 检查公钥是否已设置
if (!this.publicKey) {
throw new Error('请先设置公钥')
}
// 步骤1:检查数据长度,决定是否需要分段
const byteLength = this.getStringByteLength(data)
if (byteLength <= this.MAX_ENCRYPT_LENGTH) {
// 情况A:数据较短,不需要分段
console.log(`数据长度 ${byteLength} 字节,无需分段,直接加密`)
try {
// 使用JSEncrypt进行加密
// JSEncrypt.encrypt()内部会自动处理:
// 1. 将字符串转换为RSA需要的格式
// 2. 执行RSA加密
// 3. 将结果转换为Base64字符串
const encrypted = this.encryptor.encrypt(data)
if (!encrypted) {
throw new Error('加密失败,可能原因:密钥格式错误、数据格式问题')
}
// 返回格式:0::SEG::密文
// "0"表示不分段,解密时根据这个标识决定如何处理
return `0${this.SEGMENT_DELIMITER}${encrypted}`
} catch (error) {
console.error('加密过程出错:', error)
throw new Error(`加密失败: ${error.message}`)
}
} else {
// 情况B:数据较长,需要分段加密
console.log(`数据长度 ${byteLength} 字节,超过限制 ${this.MAX_ENCRYPT_LENGTH} 字节,开始分段加密...`)
// 步骤2:将长文本分割成多个片段
const segments = this.splitStringByByteLength(data, this.MAX_ENCRYPT_LENGTH)
console.log(`分割为 ${segments.length} 个片段`)
// 步骤3:对每个片段分别进行加密
const encryptedSegments = []
for (let i = 0; i < segments.length; i++) {
try {
console.log(`正在加密第 ${i + 1}/${segments.length} 段,长度: ${segments[i].length} 字符`)
const encrypted = this.encryptor.encrypt(segments[i])
if (!encrypted) {
throw new Error(`第 ${i + 1} 段加密失败`)
}
encryptedSegments.push(encrypted)
console.log(`第 ${i + 1} 段加密完成,密文长度: ${encrypted.length} 字符`)
} catch (error) {
console.error(`第 ${i + 1} 段加密出错:`, error)
throw new Error(`分段加密失败: 第 ${i + 1} 段 - ${error.message}`)
}
}
// 步骤4:组合加密结果
// 格式:段数::SEG::密文1::SEG::密文2::SEG::...
const result = `${segments.length}${this.SEGMENT_DELIMITER}${encryptedSegments.join(this.SEGMENT_DELIMITER)}`
console.log(`分段加密完成,总密文长度: ${result.length} 字符`)
return result
}
}
/**
* 分段解密(处理分段加密的数据)
* @param {string} encryptedData - 加密后的数据(包含分段信息)
* @returns {string} 解密后的明文数据
*
* 输入数据格式:
* - 不分段:"0::SEG::Base64密文"
* - 分段:"N::SEG::密文1::SEG::密文2::SEG::..." (N为段数)
*/
decryptLong(encryptedData) {
// 检查私钥是否已设置
if (!this.privateKey) {
throw new Error('请先设置私钥')
}
try {
// 步骤1:使用分隔符分割加密数据
// 例如:"3::SEG::密文1::SEG::密文2::SEG::密文3"
// 分割后:["3", "密文1", "密文2", "密文3"]
const parts = encryptedData.split(this.SEGMENT_DELIMITER)
// 验证分割结果
if (parts.length === 0) {
throw new Error('加密数据格式错误:分割后为空')
}
// 第一个部分是段数(字符串),需要转换为数字
const segmentCount = parseInt(parts[0], 10)
// 验证段数的有效性
if (isNaN(segmentCount)) {
throw new Error('加密数据格式错误:段数不是有效的数字')
}
// 步骤2:判断是否为分段加密
if (segmentCount === 0) {
// 情况A:不分段加密
console.log('检测到不分段加密数据')
// 验证数据格式:0::SEG::密文 → 应该有2个部分
if (parts.length !== 2) {
throw new Error(`不分段数据格式错误:预期2部分,实际${parts.length}部分`)
}
// 解密唯一的密文片段
const decrypted = this.encryptor.decrypt(parts[1])
if (!decrypted) {
throw new Error('解密失败:可能原因:密钥不匹配、数据损坏')
}
console.log('解密成功,解密后长度:', decrypted.length, '字符')
return decrypted
} else {
// 情况B:分段加密
console.log(`检测到分段加密数据,共 ${segmentCount} 个片段`)
// 验证数据完整性
// 应该有:段数 + 每个片段的密文
if (parts.length !== segmentCount + 1) {
throw new Error(`分段数据不匹配: 预期 ${segmentCount + 1} 部分,实际 ${parts.length} 部分`)
}
console.log(`开始分段解密...`)
// 步骤3:对每个密文片段分别解密
const decryptedSegments = []
for (let i = 1; i <= segmentCount; i++) {
const encryptedSegment = parts[i]
console.log(`正在解密第 ${i}/${segmentCount} 段,密文长度: ${encryptedSegment.length} 字符`)
const decrypted = this.encryptor.decrypt(encryptedSegment)
if (!decrypted) {
throw new Error(`第 ${i} 段解密失败`)
}
decryptedSegments.push(decrypted)
console.log(`第 ${i} 段解密完成,明文长度: ${decrypted.length} 字符`)
}
// 步骤4:合并所有解密后的片段
const finalResult = decryptedSegments.join('')
console.log(`分段解密完成,总明文长度: ${finalResult.length} 字符`)
return finalResult
}
} catch (error) {
console.error('解密过程出错:', error)
throw new Error(`解密失败: ${error.message}`)
}
}
/**
* 原始加密方法(用于短文本,兼容旧代码)
* @param {string} data - 要加密的明文数据
* @returns {string} Base64编码的加密结果
*
* 注意:这个方法不处理分段,仅适用于短文本
* 如果数据过长,会抛出警告,但尝试加密(可能失败)
*/
encrypt(data) {
if (!this.publicKey) {
throw new Error('请先设置公钥')
}
// 检查数据长度,给出警告
const byteLength = this.getStringByteLength(data)
if (byteLength > this.MAX_ENCRYPT_LENGTH) {
console.warn(`警告:数据长度 ${byteLength} 字节超过RSA加密限制 ${this.MAX_ENCRYPT_LENGTH} 字节,可能加密失败`)
console.warn('建议使用 encryptLong() 方法进行分段加密')
}
try {
const encrypted = this.encryptor.encrypt(data)
if (!encrypted) {
throw new Error('加密失败,可能是数据过长或密钥不匹配')
}
return encrypted
} catch (error) {
console.error('加密过程出错:', error)
throw new Error(`加密失败: ${error.message}`)
}
}
/**
* 原始解密方法(兼容旧代码)
* @param {string} encryptedData - Base64编码的加密数据
* @returns {string} 解密后的明文数据
*
* 注意:这个方法只处理不分段的加密数据
*/
decrypt(encryptedData) {
if (!this.privateKey) {
throw new Error('请先设置私钥')
}
try {
const decrypted = this.encryptor.decrypt(encryptedData)
if (!decrypted) {
throw new Error('解密失败,可能是数据损坏或密钥不匹配')
}
return decrypted
} catch (error) {
console.error('解密过程出错:', error)
throw new Error(`解密失败: ${error.message}`)
}
}
/**
* 获取RSA密钥信息
* @returns {Object} 密钥信息对象
*/
getKeyInfo() {
return {
publicKey: this.publicKey ? '已设置' : '未设置',
privateKey: this.privateKey ? '已设置' : '未设置',
maxEncryptLength: this.MAX_ENCRYPT_LENGTH,
supportsLongData: true,
description: `RSA分段加密工具,支持最大${this.MAX_ENCRYPT_LENGTH}字节明文`
}
}
/**
* 测试方法:验证加密解密功能是否正常工作
* @param {string} testData - 测试数据
* @returns {Object} 测试结果
*/
testEncryption(testData = 'Hello, RSA分段加密测试! 这是一个测试字符串,用于验证分段加密解密功能是否正常工作。') {
try {
console.log('=== RSA分段加密测试 ===')
// 1. 检查密钥设置
if (!this.publicKey || !this.privateKey) {
throw new Error('请先设置公钥和私钥')
}
// 2. 获取测试数据信息
const byteLength = this.getStringByteLength(testData)
console.log(`测试数据长度: ${testData.length} 字符, ${byteLength} 字节`)
// 3. 加密测试
console.log('开始加密测试...')
const encrypted = this.encryptLong(testData)
console.log(`加密完成,密文长度: ${encrypted.length} 字符`)
// 4. 解密测试
console.log('开始解密测试...')
const decrypted = this.decryptLong(encrypted)
// 5. 验证结果
const isSuccess = decrypted === testData
console.log(`解密验证: ${isSuccess ? '成功' : '失败'}`)
return {
success: isSuccess,
originalLength: testData.length,
encryptedLength: encrypted.length,
decryptedLength: decrypted.length,
byteLength: byteLength,
needsSegmentation: byteLength > this.MAX_ENCRYPT_LENGTH
}
} catch (error) {
console.error('测试失败:', error)
return {
success: false,
error: error.message
}
}
}
}
// 导出单例实例
// 使用单例模式的好处:
// 1. 全局只有一个实例,避免重复创建
// 2. 方便在不同模块中共享密钥状态
// 3. 统一管理加密解密操作
export default new RSASegmentUtil()
使用示例(测试代码)
// test-rsa-segment.js
import RSASegment from './src/utils/rsa-segment.js'
/**
* 演示如何使用RSA分段加密工具类
*/
async function demoRSASegment() {
console.log('=== RSA分段加密演示 ===\n')
try {
// 1. 设置RSA密钥(实际项目中应从服务器获取)
const publicKey = `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4JbJ7w5t8vX9zY7L5K5J
... 这里应该是你的公钥内容 ...
-----END PUBLIC KEY-----`
const privateKey = `-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDglsnvDm3y9f3N
... 这里应该是你的私钥内容 ...
-----END PRIVATE KEY-----`
console.log('1. 设置RSA密钥...')
RSASegment.setPublicKey(publicKey)
RSASegment.setPrivateKey(privateKey)
// 2. 获取密钥信息
const keyInfo = RSASegment.getKeyInfo()
console.log('密钥信息:', keyInfo)
// 3. 测试短文本加密(不分段)
console.log('\n2. 测试短文本加密...')
const shortText = '这是一个短文本,用于测试不分段加密。'
const encryptedShort = RSASegment.encryptLong(shortText)
console.log(`加密结果: ${encryptedShort.substring(0, 50)}...`)
const decryptedShort = RSASegment.decryptLong(encryptedShort)
console.log(`解密结果: ${decryptedShort}`)
console.log(`验证: ${shortText === decryptedShort ? '✓ 成功' : '✗ 失败'}`)
// 4. 测试长文本加密(分段)
console.log('\n3. 测试长文本加密(分段)...')
// 创建一个超过245字节的长文本
let longText = '这是一个很长的文本,用于测试RSA分段加密功能。'
while (RSASegment.getStringByteLength(longText) < 500) {
longText += ' 这是额外的内容,用于增加文本长度。'
}
console.log(`长文本长度: ${longText.length} 字符`)
console.log(`长文本字节数: ${RSASegment.getStringByteLength(longText)} 字节`)
const encryptedLong = RSASegment.encryptLong(longText)
console.log(`分段加密完成,总密文长度: ${encryptedLong.length} 字符`)
const decryptedLong = RSASegment.decryptLong(encryptedLong)
console.log(`分段解密完成,解密后长度: ${decryptedLong.length} 字符`)
console.log(`验证: ${longText === decryptedLong ? '✓ 成功' : '✗ 失败'}`)
// 5. 运行自动测试
console.log('\n4. 运行自动测试...')
const testResult = RSASegment.testEncryption(longText)
console.log('测试结果:', testResult)
} catch (error) {
console.error('演示过程中出错:', error)
}
}
// 运行演示
demoRSASegment()
关键概念总结
- 为什么需要分段?
- RSA加密限制:2048位密钥最多加密245字节
- 实际应用需求:实际数据可能远大于245字节(如JSON数据、长文本)
- 解决方案:将大数据分割成小片段,分别加密
-
分段加密流程
明文数据 → 检查长度 → 是否需要分段?
↓ (是)
分割成多个片段 → 每个片段单独RSA加密 → 组合加密结果
↓ (否)
直接RSA加密 → 添加不分段标记 -
数据格式设计
不分段: "0::SEG::Base64密文"
分段: "N::SEG::密文1::SEG::密文2::SEG::..."
- 第一个字段:段数(0表示不分段)
- 分隔符 :
::SEG::(确保不会与Base64字符冲突) - 后续字段:加密后的各个片段
- 字节 vs 字符
- 字符数 :
"你好".length = 2 - 字节数 :
"你好"的UTF-8字节数 = 6 - RSA限制针对字节数,所以需要正确计算字节长度
- 实际应用建议
- 密钥管理:私钥必须安全存储,公钥可以公开
- 性能考虑:RSA加密较慢,大数据量建议使用混合加密
- 错误处理:加密解密都可能失败,必须有完善的错误处理
- 日志记录:生产环境应该记录关键操作,方便调试
这个工具类提供了完整的RSA分段加密解决方案,可以处理任意长度的数据,同时保持向后兼容性(通过encrypt()和decrypt()方法)。
四、AES 使用
- AES核心原理
- AES(Advanced Encryption Standard)是一种对称分组密码算法,支持128/192/256位密钥长度,对应10/12/14轮加密。其加密流程包含四个关键步骤:
- 字节替换(SubBytes):通过S盒进行非线性替换增强混淆性
- 行移位(ShiftRows):对状态矩阵的行循环位移
- 列混淆(MixColumns):通过有限域乘法扩散数据
- 轮密钥加(AddRoundKey):与扩展密钥进行异或操作
- 解密过程为加密的逆操作,最后一轮省略列混淆步骤。相比DES的56位密钥,AES-128的密钥空间已达3.4×10³⁸,抗暴力破解能力显著提升
- 加密模式与填充方案
- 常见模式:
- ECB:简单分块加密,相同明文生成相同密文(适合小数据加密)
- CBC:需初始化向量(IV),前块密文影响后块加密结果(推荐通用场景)
- 填充方案:
- PKCS7:填充字节值为缺少的字节数(如缺3字节填充0x03 0x03 0x03)
- NoPadding:要求数据长度必须对齐块大小(16字节)
- 关键参数解析
| 参数 | 要求 | 安全建议 |
|---|---|---|
| 密钥 | 16/24/32字节对应AES-128/192/256 | 使用PBKDF2派生密钥 |
| IV | 16字节随机值 | 禁止重复使用 |
| 操作模式 | 优先选GCM或CBC | 禁止重复使用 |
| 填充方案 | PKCS7或ISO10126 | 确保数据块对齐 |
简单加解密示例
// 引入CryptoJS加密库(使用前需安装:npm install crypto-js)
import CryptoJS from 'crypto-js';
/**
* 基础AES加密解密工具类
* 使用AES-CBC模式,PKCS7填充方案
* 注意:密钥和IV必须是16位字符串(AES-128)
*/
class BasicAESCrypto {
/**
* AES加密方法
* @param {string} plainData - 需要加密的原始文本数据
* @param {string} key - 加密密钥,必须是16位字符串
* @param {string} iv - 初始化向量,必须是16位字符串
* @returns {string|null} 成功返回Base64格式的密文,失败返回null
*
* 加密流程说明:
* 1. 将明文转换为CryptoJS可处理的WordArray格式
* 2. 使用AES算法进行加密(CBC模式 + PKCS7填充)
* 3. 返回Base64编码的密文字符串
*/
static encrypt(plainData, key, iv) {
try {
// 使用CryptoJS的AES加密方法
const encrypted = CryptoJS.AES.encrypt(
// 参数1:将普通字符串转换为CryptoJS内部使用的WordArray二进制格式
// WordArray是CryptoJS中表示二进制数据的对象
CryptoJS.enc.Utf8.parse(plainData),
// 参数2:加密密钥,同样需要转换为WordArray格式
// 注意:AES-128要求密钥长度为16字节(16个字符)
CryptoJS.enc.Utf8.parse(key),
// 参数3:加密配置选项
{
// iv:初始化向量,CBC模式必需参数,增加加密随机性
// 必须是16字节,与密钥长度一致
iv: CryptoJS.enc.Utf8.parse(iv),
// mode:加密模式,这里使用CBC(密码块链模式)
// CBC模式每个块的加密都依赖于前一个块,安全性高于ECB
mode: CryptoJS.mode.CBC,
// padding:填充方案,使用PKCS7
// PKCS7会在数据末尾添加填充字节,确保数据长度是16字节的倍数
padding: CryptoJS.pad.Pkcs7
}
);
// 返回Base64格式的密文字符串
// CryptoJS默认返回的是CipherParams对象,toString()将其转为Base64字符串
return encrypted.toString();
} catch (error) {
// 错误处理:加密过程中出现任何异常都捕获并返回null
console.error('AES加密失败,请检查参数格式:', error);
return null;
}
}
/**
* AES解密方法
* @param {string} encryptedData - Base64格式的加密数据
* @param {string} key - 解密密钥,必须与加密时使用的密钥相同
* @param {string} iv - 初始化向量,必须与加密时使用的IV相同
* @returns {string|null} 成功返回解密后的原始文本,失败返回null
*
* 解密流程说明:
* 1. 使用AES算法解密Base64密文
* 2. 将解密结果从WordArray转换回普通字符串
* 3. 返回原始明文数据
*/
static decrypt(encryptedData, key, iv) {
try {
// 使用CryptoJS的AES解密方法
const decrypted = CryptoJS.AES.decrypt(
// 参数1:待解密的Base64格式密文
// CryptoJS会自动识别这是Base64字符串并解析
encryptedData,
// 参数2:解密密钥,必须与加密密钥完全相同
CryptoJS.enc.Utf8.parse(key),
// 参数3:解密配置选项,必须与加密配置完全一致
{
iv: CryptoJS.enc.Utf8.parse(iv),
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
}
);
// 将解密结果从WordArray二进制格式转换为UTF-8字符串
// toString(CryptoJS.enc.Utf8) 指定输出编码为UTF-8
return decrypted.toString(CryptoJS.enc.Utf8);
} catch (error) {
// 错误处理:解密失败可能因为密钥错误、数据损坏等原因
console.error('AES解密失败,请检查密钥或数据格式:', error);
return null;
}
}
}
// ==============================================
// 使用示例和测试代码
// ==============================================
/**
* 演示如何使用基础AES加密解密
*/
function demoBasicAES() {
// 定义测试数据
const originalText = 'Hello World! 这是需要加密的重要数据。'; // 原始明文
const secretKey = '1234567890123456'; // 16位密钥(AES-128要求)
const iv = 'abcdefghijklmnop'; // 16位初始化向量
console.log('=== 基础AES加密解密演示 ===');
console.log('原始文本:', originalText);
console.log('加密密钥:', secretKey);
console.log('初始化向量:', iv);
// 执行加密
const encryptedResult = BasicAESCrypto.encrypt(originalText, secretKey, iv);
console.log('加密结果(Base64):', encryptedResult);
// 执行解密
const decryptedResult = BasicAESCrypto.decrypt(encryptedResult, secretKey, iv);
console.log('解密结果:', decryptedResult);
// 验证加解密是否成功
if (decryptedResult === originalText) {
console.log('✅ 加解密测试成功!');
} else {
console.log('❌ 加解密测试失败!');
}
}
// 执行演示
// demoBasicAES();
// 导出工具类供其他模块使用
export default BasicAESCrypto;
复杂加解密示例
// 引入CryptoJS加密库(使用前需安装:npm install crypto-js)
import CryptoJS from 'crypto-js';
/**
* 带简单混淆的AES加密解密工具类
* 在基础AES加密前对密钥和IV进行简单混淆处理,增加一层安全性
* 混淆方式:每4个原始字符后插入1个固定字符'X'
*/
class ObfuscatedAESCrypto {
/**
* 简单字符串混淆处理
* @param {string} str - 需要混淆的原始字符串
* @returns {string} 混淆后的字符串
*
* 混淆原理:
* - 遍历原始字符串,每4个字符为一组
* - 在每组后面插入一个固定字符'X'
* - 例如:"12345678" → "1234X5678X"
*
* 目的:
* - 增加原始密钥和IV的复杂度
* - 防止直接观察或简单分析得到真实密钥
*/
static simpleObfuscate(str) {
let obfuscatedResult = ''; // 存储混淆后的结果
// 遍历字符串,步长为4(每4个字符处理一次)
for (let i = 0; i < str.length; i += 4) {
// 从当前位置i开始,截取最多4个字符
// substr(i, 4) 表示从索引i开始,取4个字符
// 如果剩余字符不足4个,会取到字符串末尾
const currentChunk = str.substr(i, 4);
// 将当前字符块添加到结果中,并在后面加上混淆字符'X'
obfuscatedResult += currentChunk + 'X';
}
return obfuscatedResult;
}
/**
* 去除字符串混淆(混淆的逆操作)
* @param {string} obfuscatedStr - 混淆后的字符串
* @returns {string} 还原后的原始字符串
*
* 去混淆原理:
* - 混淆后的字符串格式为:每5个字符为一组(4个原始字符 + 1个混淆字符)
* - 遍历时每5个字符取前4个,跳过混淆字符
* - 例如:"1234X5678X" → "12345678"
*/
static deobfuscate(obfuscatedStr) {
let originalResult = ''; // 存储去混淆后的原始结果
// 遍历混淆后的字符串,步长为5(每5个字符处理一次)
for (let i = 0; i < obfuscatedStr.length; i += 5) {
// 从当前位置i开始,截取4个字符(跳过第5个混淆字符)
// 如果剩余字符不足4个,会取到可用的所有字符
const originalChunk = obfuscatedStr.substr(i, 4);
// 将原始字符块添加到结果中
originalResult += originalChunk;
}
return originalResult;
}
/**
* 带混淆的AES加密方法
* @param {string} plainData - 需要加密的原始文本数据
* @param {string} key - 原始加密密钥(混淆前)
* @param {string} iv - 原始初始化向量(混淆前)
* @returns {string|null} 成功返回Base64格式的密文,失败返回null
*
* 加密流程:
* 1. 对原始密钥和IV进行混淆处理
* 2. 使用混淆后的密钥和IV进行标准AES加密
* 3. 返回Base64编码的密文
*/
static encrypt(plainData, key, iv) {
try {
// 步骤1:对密钥和IV进行混淆处理
// 混淆后的密钥和IV会变得更长,增加破解难度
const obfuscatedKey = this.simpleObfuscate(key);
const obfuscatedIv = this.simpleObfuscate(iv);
console.log('混淆前密钥:', key, '长度:', key.length);
console.log('混淆后密钥:', obfuscatedKey, '长度:', obfuscatedKey.length);
console.log('混淆前IV:', iv, '长度:', iv.length);
console.log('混淆后IV:', obfuscatedIv, '长度:', obfuscatedIv.length);
// 步骤2:使用混淆后的密钥和IV进行AES加密
const encrypted = CryptoJS.AES.encrypt(
// 将明文转换为WordArray二进制格式
CryptoJS.enc.Utf8.parse(plainData),
// 使用混淆后的密钥(转换为WordArray格式)
CryptoJS.enc.Utf8.parse(obfuscatedKey),
// 加密配置,使用混淆后的IV
{
iv: CryptoJS.enc.Utf8.parse(obfuscatedIv),
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
}
);
// 返回Base64格式的密文
return encrypted.toString();
} catch (error) {
console.error('带混淆的AES加密失败:', error);
return null;
}
}
/**
* 带混淆的AES解密方法
* @param {string} encryptedData - Base64格式的加密数据
* @param {string} key - 原始加密密钥(与加密时相同)
* @param {string} iv - 原始初始化向量(与加密时相同)
* @returns {string|null} 成功返回解密后的原始文本,失败返回null
*
* 解密流程:
* 1. 对原始密钥和IV进行相同的混淆处理
* 2. 使用混淆后的密钥和IV进行标准AES解密
* 3. 返回解密后的原始文本
*/
static decrypt(encryptedData, key, iv) {
try {
// 步骤1:对密钥和IV进行相同的混淆处理
// 必须使用与加密时完全相同的混淆算法和参数
const obfuscatedKey = this.simpleObfuscate(key);
const obfuscatedIv = this.simpleObfuscate(iv);
// 步骤2:使用混淆后的密钥和IV进行AES解密
const decrypted = CryptoJS.AES.decrypt(
// Base64格式的密文
encryptedData,
// 使用混淆后的密钥(必须与加密时相同)
CryptoJS.enc.Utf8.parse(obfuscatedKey),
// 解密配置,必须与加密配置完全一致
{
iv: CryptoJS.enc.Utf8.parse(obfuscatedIv),
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
}
);
// 将解密结果从WordArray转换为UTF-8字符串
return decrypted.toString(CryptoJS.enc.Utf8);
} catch (error) {
console.error('带混淆的AES解密失败:', error);
return null;
}
}
}
// ==============================================
// 使用示例和测试代码
// ==============================================
/**
* 演示如何使用带混淆的AES加密解密
*/
function demoObfuscatedAES() {
// 定义测试数据
const originalText = 'Hello World! 这是需要加密的重要数据。'; // 原始明文
const secretKey = '1234567890123456'; // 16位原始密钥
const iv = 'abcdefghijklmnop'; // 16位原始IV
console.log('\n=== 带混淆的AES加密解密演示 ===');
console.log('原始文本:', originalText);
console.log('原始密钥:', secretKey);
console.log('原始IV:', iv);
// 执行加密
const encryptedResult = ObfuscatedAESCrypto.encrypt(originalText, secretKey, iv);
console.log('加密结果(Base64):', encryptedResult);
// 执行解密
const decryptedResult = ObfuscatedAESCrypto.decrypt(encryptedResult, secretKey, iv);
console.log('解密结果:', decryptedResult);
// 验证加解密是否成功
if (decryptedResult === originalText) {
console.log('✅ 带混淆的加解密测试成功!');
} else {
console.log('❌ 带混淆的加解密测试失败!');
}
// 演示混淆过程
console.log('\n--- 混淆过程演示 ---');
const testKey = '1234567890123456';
const obfuscated = ObfuscatedAESCrypto.simpleObfuscate(testKey);
const deobfuscated = ObfuscatedAESCrypto.deobfuscate(obfuscated);
console.log('原始密钥:', testKey);
console.log('混淆后:', obfuscated);
console.log('去混淆后:', deobfuscated);
console.log('混淆还原是否成功:', testKey === deobfuscated);
}
// 执行演示
// demoObfuscatedAES();
// 导出工具类供其他模块使用
export default ObfuscatedAESCrypto;
五、SM3使用
SM3算法对需要加密的数据没有长度限制。该算法采用与SHA-256类似的分组处理机制,通过填充和迭代方式处理任意长度的输入数据。
该方法适用于需要遵循国家密码标准的场景:
-
政务系统数据完整性验证
-
金融交易数字签名
-
物联网设备认证
import { sm3 } from 'sm-crypto'
/**
- 使用国密SM3算法对数据进行哈希处理
- @param {string|ArrayBuffer} data - 待哈希的原始数据
- @returns {string} 十六进制格式的哈希结果
*/
// data:待哈希的原始数据,可以是字符串或ArrayBuffer等二进制格式
function generateSM3Hash(data) {
// 对消息进行SM3哈希计算,返回十六进制字符串
return sm3(data)
}
// 使用示例
const message = '需要哈希处理的数据内容'try {
const hashResult = generateSM3Hash(message)
console.log('SM3哈希结果:', hashResult)
// 输出类似 "edd9d1f2d46bdc10e4e24167c....." 的格式
} catch (error) {
console.error('SM3哈希生成失败:', error)
}
两个版本的主要区别:
-
版本一(BasicAESCrypto):
-
直接使用原始密钥和IV进行AES加密
-
代码简单,性能更好
-
适合对安全性要求不是极高的场景
-
-
版本二(ObfuscatedAESCrypto):
-
在加密前对密钥和IV进行混淆处理
-
增加了一层安全防护
-
适合对安全性要求更高的场景
-
性能稍差,但安全性更好
-
六、SHA-256使用
-
固定输出长度:无论输入数据大小,始终生成 256 位哈希值
不可逆性:无法从哈希值反推原始数据
-
雪崩效应:输入数据的微小变化会导致输出哈希值的巨大变化
-
抗碰撞性:设计上极难找到两个不同的输入生成相同的哈希值
-
高效性:计算速度快,适合处理大量数据
import { SHA256, enc } from 'crypto-js'
/**
- 使用 SHA-256 对数据进行哈希处理
- @param {string} message - 待哈希的原始数据
- @returns {string} 十六进制格式的哈希结果
*/
function generateSHA256Hash(message) {
// 对消息进行 SHA-256 哈希计算,返回 WordArray 对象
const hash = SHA256(message)
// 将二进制哈希结果转换为十六进制格式字符串 return hash.toString(enc.Hex)}
// 使用示例
const data = '需要哈希处理的数据内容'try {
const hashResult = generateSHA256Hash(data)
console.log('SHA-256 哈希结果:', hashResult)
// 输出类似 "a1b2c3d4e5f6..." 的格式
} catch (error) {
console.error('哈希生成失败:', error)
}// Base64 格式输出
const base64Hash = SHA256(message).toString(enc.Base64)
七、HMAC-SHA256使用
crypto-js 库中的 HmacSHA256 函数对需要加密的数据没有长度限制。
HMAC-SHA256算法在设计上可以处理任意长度的输入数据。其内部工作机制是先将数据分割成固定大小的块(SHA-256使用512位的块大小),然后逐块处理。无论您的原始消息是几个字节还是几个GB,该算法都能通过迭代处理的方式生成唯一的256位哈希值。
这种无限制特性使得 HMAC-SHA256 非常适合用于验证各种规模数据的完整性和真实性,包括短文本消息和大型文件。
import { HmacSHA256, enc } from 'crypto-js'
/**
* 使用 HMAC-SHA256 对消息进行签名
* @param {string} message - 待签名的原始消息
* @param {string} secretKey - 用于签名的密钥
* @returns {string} Base64 编码的签名结果
*/
// message:待签名的原始数据,可以是字符串或对象
// secretKey:用于生成签名的密钥,应妥善保管
function generateHMACSHA256Signature(message, secretKey) {
// 使用密钥对消息进行 HMAC-SHA256 加密
// HmacSHA256() 返回 WordArray 对象(crypto-js 内部的二进制数据容器)
const hash = HmacSHA256(message, secretKey)
// 将二进制哈希结果转换为 Base64 格式字符串
return enc.Base64.stringify(hash)
}
// 使用示例
const SECRET_KEY = 'your-256-bit-secret-key-here' // 实际项目中应从环境变量获取
const message = '需要签名的数据内容'
try {
const signature = generateHMACSHA256Signature(message, SECRET_KEY)
console.log('HMAC-SHA256 签名结果:', signature)
} catch (error) {
console.error('签名生成失败:', error)
}
八、MD5使用
-
固定输出长度:无论输入数据大小,始终生成128位(16字节)的哈希值,通常表示为32位十六进制字符串。
-
不可逆性:从MD5哈希值无法反向推导出原始数据,保证了一定程度的安全性。
-
安全性说明:
尽管MD5曾被广泛使用,但现在它已不再被认为是安全的加密算法,2004年,研究人员公布了MD5的第一个实际碰撞攻击,此后MD5便逐渐退出了高强度加密应用的舞台。对于安全性要求较高的应用,推荐使用更安全的哈希算法如SHA-256import MD5 from 'crypto-js/md5'
/**
- 使用MD5算法对数据进行哈希处理
- @param {string} data - 待哈希的原始数据
- @returns {string} 十六进制格式的哈希结果
*/
function generateMD5Hash(data) {
// 对消息进行MD5哈希计算,返回WordArray对象
const hash = MD5(data)
// 将二进制哈希结果转换为十六进制格式字符串 return hash.toString()}
// 使用示例
const message = '需要加密的数据内容'try {
const hashResult = generateMD5Hash(message)
console.log('MD5加密结果:', hashResult)
// 输出类似 "bee0771321a8b......" 的32位十六进制字符串
} catch (error) {
console.error('MD5加密失败:', error)
}
九、DES使用
使用时需要注意,DES算法因其56位密钥长度较短,已逐渐被更安全的AES算法取代,建议在安全性要求较高的场景中使用AES替代方案。
-
作用:使用56位密钥对数据进行块加密(64位块大小),现因安全性问题逐渐被AES取代
-
核心参数
-
密钥:8字节(64位),不足时会自动补零
-
模式与填充:需配合 mode 和 pad 使用
-
DES.encrypt 加密
-
DES.decrypt 解密
-
mode:加密模式
- 作用:定义块加密算法(如DES/AES)如何处理数据块之间的关系
- 常见模式:
- CBC(默认):每个块与前一个密文块异或,需初始化向量(IV)
- ECB:简单分块加密,相同明文生成相同密文,安全性较低
pad:填充方案
-
作用:当数据长度不是块大小的整数倍时,填充额外字节以满足要求
-
常用方案:
-
Pkcs7:填充字节值为缺少的字节数(如缺3字节则填充0x03 0x03 0x03)
-
NoPadding:不填充,要求数据长度必须对齐
import { enc, DES, mode, pad } from 'crypto-js'
/**
- DES加密函数
- @param {string} plainText - 待加密的明文
- @param {string} key - 加密密钥(8字节)
- @returns {string} Base64格式的密文
*/
function desEncrypt(plainText, key) {
// 将UTF-8格式的密钥转换为WordArray对象
// 注意:DES密钥长度应为8字节(64位),不足时会自动补全
const keyHex = enc.Utf8.parse(key)
// 配置加密参数 const encryptOptions = { mode: mode.ECB, // 使用ECB加密模式 padding: pad.Pkcs7 // 使用PKCS#7填充标准 } // 执行DES加密操作 const encrypted = DES.encrypt(plainText, keyHex, encryptOptions) // 将加密结果转换为Base64字符串 return encrypted.toString()}
/**
- DES解密函数
- @param {string} encryptedText - 待解密的Base64字符串
- @param {string} key - 解密密钥(8字节)
- @returns {string} UTF-8格式的明文
*/
function desDecrypt(encryptedText, key) {
// 将字符串格式的密钥转换为二进制格式
const keyHex = enc.Utf8.parse(key)
// 将Base64编码的密文字符串解码为WordArray对象 const decodedData = enc.Base64.parse(encryptedText) // 配置解密参数对象 const decryptOptions = { mode: mode.ECB, // 使用ECB加密模式 padding: pad.Pkcs7 // 使用PKCS#7填充标准 } // 执行DES解密操作 const decrypted = DES.decrypt( { ciphertext: decodedData }, keyHex, decryptOptions ) // 将解密结果转换为UTF-8字符串 return decrypted.toString(enc.Utf8)}
// 使用示例
const testKey = '12345678' // 8字节密钥
const originalMessage = 'Hello, DES加密测试!'// 加密演示
try {
const encrypted = desEncrypt(originalMessage, testKey)
console.log('加密结果:', encrypted)// 解密演示 const decrypted = desDecrypt(encrypted, testKey) console.log('解密结果:', decrypted) console.log('加解密验证:', decrypted === originalMessage)} catch (error) {
console.error('DES操作失败:', error)
}// 导出函数供其他模块使用
export { desEncrypt, desDecrypt }
十、各库核心功能与引入方式
-
crypto-js 是一个纯 JavaScript 实现的加密算法库,支持多种对称加密(如 AES、DES)、哈希算法(如 MD5、SHA 系列)和消息认证码(如 HMAC),适用于浏览器和 Node.js 环境。
-
js-base64 是一个用于 Base64 编码/解码的 JavaScript 库,支持浏览器和 Node.js 环境。
-
sm-crypto这是一个专门实现国密算法(SM系列)的JavaScript库。
-
功能互补:这三个库在功能上是互补的。crypto-js 覆盖了国际通用算法(AES, SHA-256, MD5, DES等)sm-crypto 专门负责国密算法。而 js-base64 提供了纯粹的Base64转换功能。
十一、补充知识点
CryptoJS加密库常用方法详解
- 转换逻辑
-
parse:将 可读格式(字符串/Base64/Hex)→ WordArray(二进制中间态)
-
stringify:将 WordArray → 可读格式(Base64/Hex/字符串)
-
相互转换示例
// 引入CryptoJS加密库(使用前需安装:npm install crypto-js) import CryptoJS from 'crypto-js'; // 字符串 → WordArray const wordArray = CryptoJS.enc.Utf8.parse("Hello"); // WordArray → 字符串 const str = CryptoJS.enc.Utf8.stringify(wordArray); // Base64 → WordArray const bytes = CryptoJS.enc.Base64.parse("SGVsbG8="); // WordArray → Base64 const base64 = CryptoJS.enc.Base64.stringify(bytes); // Hex → WordArray const hexBytes = CryptoJS.enc.Hex.parse("48656c6c6f"); // WordArray → Hex const hexStr = CryptoJS.enc.Hex.stringify(hexBytes); // Base64 → Hex const hexFromBase64 = CryptoJS.enc.Hex.stringify( CryptoJS.enc.Base64.parse("SGVsbG8=") ); // Hex → Base64 const base64FromHex = CryptoJS.enc.Base64.stringify( CryptoJS.enc.Hex.parse("48656c6c6f") );
3. 详细说明
-
什么是编码?想象一下:计算机不懂"你好"这样的文字,它只懂0和1。所以我们需要规则把文字变成0和1,这个规则就是"编码"。
- UTF-8:把人类文字变成电脑数字
- Base64:把二进制变成字母数字(A-Z,a-z,0-9,+,/)
- Hex(十六进制):用0-9和A-F表示二进制
-
举个现实例子
你的名字:张三 用不同方式表示: - 中文写法:张三 - 拼音写法:Zhang San - 英文写法:John - 编号写法:员工001号 同一个东西,不同表示方法! -
-
字符串 ↔ WordArray
// 字符串 → WordArray
const wordArray = CryptoJS.enc.Utf8.parse("Hello");
拆解理解:
CryptoJS- 这是一个工具箱(就像Photoshop是修图工具).enc- "编码"部门(encoding的缩写).Utf8- 使用UTF-8这个翻译规则.parse("Hello")- "解析"(翻译)这个词
实际发生了什么:
"Hello"这个单词 ↓ UTF-8编码翻译 变成:72(H) 101(e) 108(l) 108(l) 111(o) 这串数字 ↓ 打包成WordArray格式 变成:{words: [1214606444, 1864398703], sigBytes: 5}为什么这样做?
因为加密算法需要数字,不能直接处理字母"H"。
// WordArray → 字符串 const str = CryptoJS.enc.Utf8.stringify(wordArray);拆解理解:
.stringify()- 和parse()相反,是"反解析"- 把数字变回文字
-
-
-
Base64 ↔ WordArray
// Base64 → WordArray
const bytes = CryptoJS.enc.Base64.parse("SGVsbG8=");
什么是Base64?
Base64是一种用64个字符表示所有二进制数据的方法:
- 小写字母:a-z(26个)
- 大写字母:A-Z(26个)
- 数字:0-9(10个)
- 两个特殊符号:+ 和 /(2个)
- 有时候用=结尾(补齐用)
"SGVsbG8=" 是什么?
"Hello" ↓ 变成数字:72 101 108 108 111 ↓ 变成二进制:01001000 01100101 01101100 01101100 01101111 ↓ 每6位一组:010010 000110 010101 101100 011011 000110 1111??(不够补0) ↓ 查Base64表: 010010(18)→S 000110(6)→G 010101(21)→V 101100(44)→s 011011(27)→b 000110(6)→G 111100(60)→8 000000(补的)→= 结果:"SGVsbG8=" // WordArray → Base64 const base64 = CryptoJS.enc.Base64.stringify(bytes);这是反向操作,把WordArray变成Base64字符串。
-
-
-
Hex ↔ WordArray
// Hex → WordArray
const hexBytes = CryptoJS.enc.Hex.parse("48656c6c6f");
什么是Hex(十六进制)?
- 十进制:0-9(我们平时用的)
- 二进制:0和1(电脑用的)
- 十六进制:0-9和A-F(程序员用的)
为什么用十六进制?
因为二进制太长了:
字母"H"的二进制:01001000(8位) 字母"H"的十六进制:48(2位) 短很多,容易看!"48656c6c6f" 是什么?
48 = H 65 = e 6c = l 6c = l 6f = o 所以 "48656c6c6f" = "Hello" // WordArray → Hex const hexStr = CryptoJS.enc.Hex.stringify(hexBytes);反向操作,把WordArray变成十六进制字符串。
-
-
-
Base64 ↔ Hex
// Base64 → Hex
const hexFromBase64 = CryptoJS.enc.Hex.stringify(
CryptoJS.enc.Base64.parse("SGVsbG8=")
);
分步看:
CryptoJS.enc.Base64.parse("SGVsbG8=")- 把Base64变成WordArrayCryptoJS.enc.Hex.stringify(...)- 把WordArray变成十六进制- 结果:
"48656c6c6f"
就像:
中文"你好" → 拼音"ni hao" → 英文"hello" Base64"SGVsbG8=" → 数字格式 → Hex"48656c6c6f" // Hex → Base64 const base64FromHex = CryptoJS.enc.Base64.stringify( CryptoJS.enc.Hex.parse("48656c6c6f") );反向操作:
CryptoJS.enc.Hex.parse("48656c6c6f")- 把十六进制变成WordArrayCryptoJS.enc.Base64.stringify(...)- 把WordArray变成Base64- 结果:
"SGVsbG8="
-
-
为什么需要这么多转换?
不同场景用不同格式:
// 场景1:用户输入密码(用UTF-8) const 用户密码 = "mypassword123"; const 可以加密的格式 = CryptoJS.enc.Utf8.parse(用户密码); // 场景2:加密后存数据库(用Base64,因为短) const 加密结果 = 加密算法(可以加密的格式); const 存数据库的格式 = CryptoJS.enc.Base64.stringify(加密结果); // 场景3:调试时查看数据(用Hex,看得清楚) console.log("加密后的Hex:", CryptoJS.enc.Hex.stringify(加密结果)); // 场景4:从数据库读取(Base64转回) const 从数据库读取 = "U2FsdGVkX1/..."; const 准备解密的格式 = CryptoJS.enc.Base64.parse(从数据库读取); const 解密结果 = 解密算法(准备解密的格式); const 原密码 = CryptoJS.enc.Utf8.stringify(解密结果); -
完整生活化例子
想象你在寄一封加密信:
// 1. 写信(用户输入) const 信的内容 = "明天下午3点见"; // → 你脑子里是中文 // 2. 翻译成摩斯密码(UTF-8 → WordArray) const 摩斯密码本 = CryptoJS.enc.Utf8.parse(信的内容); // → 现在变成:.... --- .--. . (这是比喻) // 3. 用密码本加密(加密操作) const 加密后的摩斯密码 = 加密算法(摩斯密码本); // → 变成乱码:..- ..-. .-.. .-.. // 4. 写成明码电报(WordArray → Base64) const 电报格式 = CryptoJS.enc.Base64.stringify(加密后的摩斯密码); // → 变成:SGVsbG8gV29ybGQ= // 这个可以写在纸上发出去 // 5. 对方收到电报(Base64 → WordArray) const 收到的电报 = "SGVsbG8gV29ybGQ="; const 电报转摩斯 = CryptoJS.enc.Base64.parse(收到的电报); // 6. 用密码本解密 const 解密后的摩斯 = 解密算法(电报转摩斯); // 7. 摩斯转回中文(WordArray → UTF-8) const 看到的内容 = CryptoJS.enc.Utf8.stringify(解密后的摩斯); // → "明天下午3点见" -
总结表:
操作 比喻 目的 Utf8.parse()中文 → 摩斯密码 把文字变成电脑能处理的数字 Utf8.stringify()摩斯密码 → 中文 把数字变回人类能读的文字 Base64.parse()电报码 → 摩斯密码 把传输格式变成处理格式 Base64.stringify()摩斯密码 → 电报码 把处理格式变成容易传输的格式 Hex.parse()十六进制码 → 摩斯密码 调试时把显示格式变成处理格式 Hex.stringify()摩斯密码 → 十六进制码 把处理格式变成容易看的格式 最核心的一句话 :CryptoJS这个库的所有加密功能都只认识
WordArray这种格式,所以无论你的数据原来是什么样子,都要先变成WordArray,处理完再变回去你需要的格式。
js-base64 库常用方法详解
-
js-base64 是一个用于 Base64 编码/解码的 JavaScript 库,支持浏览器和 Node.js 环境。
导入方式
import { Base64 } from 'js-base64'-
字符串 ↔ Base64 编码
// 将 UTF-8 字符串编码为 Base64 格式
const encoded = Base64.encode('Hello 世界');
// 返回:"SGVsbG8g5LiW55WM"// 将 Base64 字符串解码为 UTF-8 原文
const decoded = Base64.decode("SGVsbG8g5LiW55WM");
// 返回:"Hello 世界" -
兼容原生方法
// 兼容原生 atob,但支持 Unicode(原生 atob 仅限 ASCII)
const result1 = Base64.atob("SGVsbG8=");
// 返回:"Hello"// 兼容原生 btoa,支持 Unicode 扩展
const result2 = Base64.btoa("Hello");
// 返回:"SGVsbG8=" -
Unicode(字符抽象层)
- 定义:跨语言的字符编码标准,为全球字符分配唯一数字标识(码点)
- 示例:码点(U+XXXX)
- 作用:为每个字符提供全球唯一的"身份证号"
- UTF-8(存储与传输层)
- 定义:将Unicode码点转换为可变长度的字节序列(1-4字节),兼容ASCII
- 示例:字节序列(0xE4 0xBD 0xA0)
- 特点 :
- 英文字符:1字节
- 中文字符:3字节
- 表情符号:4字节
- ASCII(基础字符集)
- 定义:使用7位二进制(0-127)表示基础英文字符
- 包含内容 :
- 英文大小写字母(A-Z, a-z)
- 数字(0-9)
- 标点符号
- 控制字符(换行LF、回车CR等)
- Base64(文本安全传输层)
-
定义:将任意二进制数据编码为64个ASCII字符组成的文本字符串
-
字符集:A-Z, a-z, 0-9, +, / 共64个字符
-
填充符:= (用于满足4字节分组要求)
-
重要性质 :Base64是编码算法,不是加密算法
-
转换流程图示
用户输入字符 → Unicode码点 → UTF-8字节 → Base64文本 ↓ ↓ ↓ ↓ "你好" → [U+4F60, → E4 BD A0 → 5L2g U+597D] E5 A5 BD 5aW9详细解释
第一层:输入层
- 作用:接收用户输入的各种字符
- 处理:将字符转换为Unicode码点序列
- 示例 :
"你好"→[U+4F60, U+597D]
第二层:存储层
- 作用:优化存储空间,兼容历史系统
- 处理:Unicode码点 → UTF-8编码
- 示例 :
[U+4F60, U+597D]→E4 BD A0 E5 A5 BD - 优势 :
- 英文保持1字节,节省空间
- 兼容旧的ASCII系统
第三层:传输层
- 作用:确保在仅支持ASCII的协议中无损传输
- 处理:UTF-8二进制 → Base64编码
- 示例 :
E4 BD A0 E5 A5 BD→"5L2g5aW9" - 应用场景 :
- 电子邮件附件
- HTTP传输
- 数据URL
-
二进制数据 → Base64
// 创建Uint8Array二进制数据
const bytes = new Uint8Array([72, 101, 108, 108, 111]);
// 72=H, 101=e, 108=l, 108=l, 111=o// 将Uint8Array转为Base64字符串
const base64 = Base64.fromUint8Array(bytes);
// 返回:"SGVsbG8=" -
Base64 → 二进制数据
// 将Base64字符串还原为Uint8Array
const bytes = Base64.toUint8Array("SGVsbG8=");
// 返回:Uint8Array [72, 101, 108, 108, 111]
实际应用场景
// 场景:处理图片文件 const fileInput = document.getElementById('file-input'); fileInput.addEventListener('change', async (event) => { const file = event.target.files[0]; // 读取为ArrayBuffer const arrayBuffer = await file.arrayBuffer(); // 转换为Uint8Array const uint8Array = new Uint8Array(arrayBuffer); // 转为Base64(可用于预览或上传) const base64String = Base64.fromUint8Array(uint8Array); const dataURL = `data:${file.type};base64,${base64String}`; // 显示预览 document.getElementById('preview').src = dataURL; });URL安全处理
问题背景
Base64标准字符集中的
+、/、=在URL中有特殊含义:+在URL中会被解释为空格/是URL路径分隔符=是查询参数分隔符
解决方案
// 生成URL安全的Base64(替换+/为-_,省略填充符=) const safe = Base64.encodeURI("Hello?"); // 返回:"SGVsbG8_" // 注意:? 字符的Base64编码是 "Pw==",但URL安全版是 "_" // 解码URL安全的Base64字符串 const str = Base64.decodeURI("SGVsbG8_"); // 返回:"Hello?"转换规则对照表
标准Base64 URL安全Base64 说明 +-避免被解释为空格 /_避免被解释为路径分隔符 =(省略) 避免干扰参数解析 扩展功能
-
扩展字符串原型
// 为原生String添加扩展方法
Base64.extendString();// 使用扩展方法
const encoded = "Hello".toBase64();
// 返回:"SGVsbG8="const decoded = "SGVsbG8=".fromBase64();
// 返回:"Hello"(需确认具体方法名) -
验证Base64格式
// 验证字符串是否为合法Base64格式
const isValid = Base64.isValid("SGVsbG8=");
// 返回:trueconst isInvalid = Base64.isValid("Invalid@Base64");
// 返回:false
验证规则
- 字符集检查 :只能包含
A-Za-z0-9+/=字符 - 长度检查:长度必须是4的倍数
- 填充符检查 :
=只能出现在末尾,最多2个
关键注意事项
-
Unicode支持差异
// 原生atob/btoa的问题
try {
window.btoa("你好"); // 报错:InvalidCharacterError
} catch (e) {
console.error("原生方法不支持中文");
}// js-base64的解决方案
const safeResult = Base64.btoa("你好"); // "5L2g5aW9" ✅
const decoded = Base64.atob("5L2g5aW9"); // "你好" ✅ -
填充符处理
// URL安全编码会移除 = 填充符
const standard = Base64.encode("Hello"); // "SGVsbG8="
const urlSafe = Base64.encodeURI("Hello"); // "SGVsbG8"// 解码时需要正确处理
const decoded1 = Base64.decodeURI("SGVsbG8"); // "Hello" ✅
const decoded2 = Base64.decode("SGVsbG8"); // 可能出错 ❌// 最佳实践:编解码使用同一方法
const original = "Hello World";
const encoded = Base64.encodeURI(original); // 编码
const decoded = Base64.decodeURI(encoded); // 解码 ✅ -
性能优化建议
// 场景:频繁处理二进制数据
// 不推荐:通过字符串中转(内存开销大)
function processDataSlow(data) {
// 创建临时字符串
const binaryString = String.fromCharCode(...data);
return btoa(binaryString);
}// 推荐:直接使用Uint8Array方法
function processDataFast(data) {
// 直接处理Uint8Array,避免中间字符串
return Base64.fromUint8Array(data);
}// 性能对比
const largeData = new Uint8Array(1024 * 1024); // 1MB数据console.time("慢速方法");
processDataSlow(largeData); // 可能消耗更多内存和时间
console.timeEnd("慢速方法");console.time("快速方法");
Base64.fromUint8Array(largeData); // 更高效
console.timeEnd("快速方法");
总结对比表
功能 原生方法 js-base64方法 优势 Unicode支持 ❌ 仅ASCII ✅ 完整支持 可处理中文、表情等 错误处理 ❌ 抛出异常 ✅ 优雅处理 更好的兼容性 URL安全 ❌ 不支持 ✅ 内置支持 方便URL传输 二进制支持 ❌ 需转换 ✅ 直接支持 性能更优 格式验证 ❌ 不支持 ✅ 提供验证 增强健壮性 总结表
方法 比喻 用途 Base64.encode()中文 → 英语 把任何文本变成Base64 Base64.decode()英语 → 中文 把Base64变回原文 Base64.fromUint8Array()图片二进制 → Base64 处理文件、图片 Base64.toUint8Array()Base64 → 图片二进制 下载文件、处理二进制 Base64.encodeURI()准备URL参数 把Base64变成URL安全版本 Base64.decodeURI()读取URL参数 把URL安全Base64转回 Base64.extendString()给字符串"超能力" 让字符串直接调用toBase64() 核心记忆点:
- Base64不是加密,只是编码(就像把中文翻译成英语,谁都能翻译回来)
- 用UTF-8处理多语言,避免乱码
- URL中要用encodeURI/decodeURI,避免特殊字符问题
- 处理文件用Uint8Array方法,效率更高
最佳实践建议
- 统一编码标准:前后端使用相同的Base64库
- 明确使用场景 :
- 普通文本:
Base64.encode()/decode() - URL传输:
Base64.encodeURI()/decodeURI() - 二进制数据:
Base64.fromUint8Array()/toUint8Array()
- 普通文本:
- 安全提醒:Base64是编码,不是加密,敏感数据需额外加密
- 性能考虑:处理大文件时使用分块处理,避免内存溢出
-
URL编码解码详解:escape、encodeURI、encodeURIComponent
三种编码方式的区别与对比
| 特性 | escape() | encodeURI() | encodeURIComponent() |
|---|---|---|---|
| 编码范围 | 非ASCII字符、空格等 | 除了完整URI保留字符外的所有字符 | 除了字母数字和少数符号外的所有字符 |
| 用途 | 已废弃,历史遗留 | 编码整个URI | 编码URI组件(查询参数) |
| 空格编码 | %20 | %20 | %20 |
| 中文字符 | %u4E16%u754C(格式) | %E4%B8%96%E7%95%8C | %E4%B8%96%E7%95%8C |
| 特殊字符处理 | 不编码:@*_±./ | 不编码::/?#[]@!$&'()*+,;= |
不编码:-_.!~*'() |
| 是否推荐使用 | ❌ 已废弃 | ✅ 编码完整URL | ✅ 编码URL参数 |
-
1、escape() / unescape() - 已废弃
-
编码示例
// 编码 - 不推荐使用 const encoded1 = escape("Hello 世界!"); console.log(encoded1); // "Hello%20%u4E16%u754C!" // 解码 const decoded1 = unescape("Hello%20%u4E16%u754C!"); console.log(decoded1); // "Hello 世界!" // 特殊字符处理 console.log(escape("@#$")); // "@%23%24" console.log(escape("abc123")); // "abc123"不推荐使用的原因:
- 非标准格式 :对非ASCII字符使用
%uXXXX格式(如%u4E16) - RFC不一致:不符合URI标准(RFC 3986)
- 浏览器差异:不同浏览器实现可能有差异
- 不处理URI保留字符 :如
#,?,&等
- 非标准格式 :对非ASCII字符使用
-
2、encodeURI() / decodeURI() - 编码整个URL
-
编码示例
// 编码整个URL
const url = "https://example.com/你好 world?param=值#section";
const encodedURI = encodeURI(url);
console.log(encodedURI);
// "https://example.com/你好 world?param=值#section"// 解码
const decodedURI = decodeURI(encodedURI);
console.log(decodedURI === url); // true// 不编码的字符
console.log(encodeURI(":/?#[]@!&'()*+,;=")); // ":/?#[]@!&'()*+,;=" (全部不编码)
使用场景:
// 正确:编码整个URL const fullURL = "https://example.com/搜索?q=JavaScript 教程"; const safeURL = encodeURI(fullURL); // "https://example.com/%E6%90%9C%E7%B4%A2?q=JavaScript%20%E6%95%99%E7%A8%8B" // 错误:只编码查询参数(会导致问题) const badURL = "https://example.com/搜索?q=" + encodeURI("JavaScript 教程"); // 注意:路径中的"搜索"没有编码! -
-
3、 encodeURIComponent() / decodeURIComponent() - 编码URL组件
``` // 编码查询参数值 const paramValue = "JavaScript & TypeScript 教程"; const encodedParam = encodeURIComponent(paramValue); console.log(encodedParam); // "JavaScript%20%26%20TypeScript%20%E6%95%99%E7%A8%8B" // 解码 const decodedParam = decodeURIComponent(encodedParam); console.log(decodedParam); // "JavaScript & TypeScript 教程" // 对比 encodeURI console.log(encodeURI("&")); // "&" (不编码) console.log(encodeURIComponent("&")); // "%26" (编码) ``` **使用场景:** ``` // 构建带参数的URL function buildURL(base, params) { const queryString = Object.keys(params) .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}` ) .join('&'); return `${base}?${queryString}`; } const url = buildURL("https://api.example.com/search", { q: "JavaScript 教程", page: "1", sort: "relevance", filter: "type=free&level=beginner" }); console.log(url); // "https://api.example.com/search?q=JavaScript%20%E6%95%99%E7%A8%8B&page=1&sort=relevance&filter=type%3Dfree%26level%3Dbeginner" ```总结与最佳实践
场景 推荐方法 说明 编码整个URL encodeURI()保留URL结构字符(😕?#等) 编码查询参数 encodeURIComponent()编码所有特殊字符 编码URL片段 encodeURIComponent()编码#后面的内容 现代开发 URL和URLSearchParamsAPI自动处理编码,更安全 Base64数据在URL中 Base64.encodeURI()URL安全的Base64编码 已废弃 escape()/unescape()不要在新项目中使用 重要提醒
- 始终解码用户输入:从URL获取的参数一定要解码
- 防止注入攻击:对用户输入进行适当的验证和清理
- 注意编码一致性:前后端使用相同的编码/解码策略
- 考虑长度限制:URL有长度限制,大数据应使用POST请求
- 处理异常:解码可能失败,要有错误处理机制
记住:编码是为了安全传输,解码是为了正确解析。选择合适的编码方式,确保数据在传输过程中不会丢失或损坏。