1. 项目背景与核心需求
在Vue项目中实现数据加密是一个常见的安全需求,特别是涉及敏感信息传输时。SM4作为国密算法标准,相比AES等国际通用算法更适合国内业务场景。gm-crypto是一个专门支持国密算法的JavaScript加密库,在Vue生态中逐渐成为处理SM4加密的首选方案。
我最近在金融类Vue项目中实际应用了gm-crypto进行SM4加密,发现它能完美兼容Vue2/Vue3,且加密性能优于同类库。下面将完整分享从环境配置到实战应用的全过程,包含多个实际开发中容易踩坑的细节。
2. 环境准备与库选型
2.1 为什么选择gm-crypto
对比热词中提到的sm-crypto,gm-crypto有以下优势:
- 更完整的国密算法支持(SM2/SM3/SM4)
- 针对Web环境优化,体积更小(min版仅78KB)
- 提供TypeScript类型定义
- 活跃的issue响应(实测24小时内得到维护者回复)
2.2 安装配置
# 通过npm安装(推荐) npm install gm-crypto --save # 或使用yarn yarn add gm-crypto注意:如果遇到网络问题,可配置国内镜像源。但绝对不要尝试任何违规的网络访问方式,所有操作必须遵守国家法律法规。
3. SM4加密核心实现
3.1 基础加密示例
import { SM4 } from 'gm-crypto' const key = '0123456789abcdeffedcba9876543210' // 16字节十六进制字符串 const plaintext = '需要加密的数据' // ECB模式加密 const encrypted = SM4.encrypt(plaintext, key, { inputEncoding: 'utf8', outputEncoding: 'hex' }) // ECB模式解密 const decrypted = SM4.decrypt(encrypted, key, { inputEncoding: 'hex', outputEncoding: 'utf8' })关键参数说明:
- key:必须为32个字符的十六进制字符串(16字节)
- inputEncoding/outputEncoding:支持'utf8'、'hex'、'base64'
3.2 更安全的CBC模式实现
实际项目中推荐使用CBC模式,需要额外提供iv参数:
const iv = '1234567890abcdef1234567890abcdef' // 16字节十六进制字符串 const encrypted = SM4.encrypt(plaintext, key, { iv, mode: SM4.constants.CBC, inputEncoding: 'utf8', outputEncoding: 'base64' })4. Vue项目集成实践
4.1 全局封装为Vue插件
在src/utils/crypto.js中创建封装:
import { SM4 } from 'gm-crypto' const CryptoUtil = { install(Vue) { Vue.prototype.$sm4 = { encrypt(plaintext, key, iv = null) { const options = { inputEncoding: 'utf8', outputEncoding: 'base64' } if (iv) { options.iv = iv options.mode = SM4.constants.CBC } return SM4.encrypt(plaintext, key, options) }, // 解密方法同理 } } } export default CryptoUtil在main.js中引入:
import CryptoUtil from '@/utils/crypto' Vue.use(CryptoUtil)4.2 组件中使用示例
<template> <div> <button @click="handleEncrypt">加密数据</button> </div> </template> <script> export default { methods: { handleEncrypt() { const key = this.$store.state.cryptoKey // 从Vuex获取密钥 const encrypted = this.$sm4.encrypt('敏感数据', key) console.log('加密结果:', encrypted) } } } </script>5. 关键问题与解决方案
5.1 密钥管理方案
根据项目安全级别可选择:
- 前端硬编码(仅适用于低安全要求场景)
- 通过接口动态获取(推荐)
- 结合RSA非对称加密传输SM4密钥(高安全要求)
5.2 常见报错处理
| 错误信息 | 原因分析 | 解决方案 |
|---|---|---|
| Invalid key length | 密钥格式错误 | 确保是32字符十六进制字符串 |
| Invalid iv length | IV向量错误 | CBC模式需要16字节IV |
| Input not a string | 输入数据类型错误 | 检查inputEncoding参数 |
5.3 性能优化技巧
- 加密大文件时使用Web Worker避免阻塞UI
- 多次加密相同数据可缓存加密结果
- 使用base64输出比hex节省约1/3空间
6. 安全增强实践
6.1 防调试保护
在vue.config.js中添加:
configureWebpack: { devtool: process.env.NODE_ENV === 'production' ? false : 'source-map' }6.2 混淆加固
建议配合webpack-obfuscator等工具:
const WebpackObfuscator = require('webpack-obfuscator') module.exports = { configureWebpack: { plugins: [ new WebpackObfuscator({ rotateStringArray: true }, ['excluded_bundle_name.js']) ] } }7. 与后端联调要点
7.1 Java端示例
确保后端使用相同参数:
// Spring Boot示例 import org.bouncycastle.jce.provider.BouncyCastleProvider; import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; import javax.crypto.spec.IvParameterSpec; public class SM4Util { static { Security.addProvider(new BouncyCastleProvider()); } public static String encrypt(String content, String key, String iv) throws Exception { Cipher cipher = Cipher.getInstance("SM4/CBC/PKCS5Padding", "BC"); SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), "SM4"); IvParameterSpec ivSpec = new IvParameterSpec(iv.getBytes()); cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec); byte[] encrypted = cipher.doFinal(content.getBytes(StandardCharsets.UTF_8)); return Base64.getEncoder().encodeToString(encrypted); } }7.2 联调检查清单
- 确认双方使用的模式(ECB/CBC)
- 统一密钥和IV的生成规则
- 对齐字符编码(建议全链路UTF-8)
- 协商好输出格式(hex/base64)
8. 扩展应用场景
8.1 加密URL参数
export function encryptParams(params) { const key = localStorage.getItem('sm4_key') return { data: this.$sm4.encrypt(JSON.stringify(params), key), timestamp: Date.now() } }8.2 本地存储加密
Vue.prototype.$secureStorage = { setItem(key, value) { const encrypted = this.$sm4.encrypt(value, storageKey) localStorage.setItem(key, encrypted) }, getItem(key) { const encrypted = localStorage.getItem(key) return encrypted ? this.$sm4.decrypt(encrypted, storageKey) : null } }在实际项目中使用时发现,iOS Safari浏览器对localStorage的访问有特殊限制,需要额外处理QuotaExceededError异常。建议对大于2MB的数据改用IndexedDB存储加密后的内容。