CryptoJS 是纯 JavaScript 实现的加密算法库(无需依赖后端),在 CRM 系统中主要用于敏感数据加密(如客户手机号、订单金额、登录密码)、接口参数签名、本地存储数据加密等场景。下面从「核心集成→常用加密算法→CRM 实战场景→避坑指南」全维度讲解,贴合 Vue3 + TS 开发。
一、核心定位(CRM 为什么需要加密?)
CRM 系统存储大量敏感商业数据,CryptoJS 解决的核心安全问题:
- 本地存储加密:localStorage/sessionStorage 存储的用户信息、筛选条件等,避免明文泄露;
- 接口参数签名:请求参数加密 / 签名,防止接口被篡改、抓包伪造请求;
- 敏感数据脱敏:客户手机号、身份证号等前端加密后再传给后端;
- 临时数据加密:如导出 Excel 的临时密钥、分享链接的参数加密。
CryptoJS 支持的核心算法(CRM 高频使用):
表格
算法类型 | 用途 | 示例场景 |
AES | 对称加密(加解密用同一密钥) | 本地存储敏感数据、接口参数加密 |
MD5 | 哈希摘要(不可逆) | 密码加密、参数签名、文件校验 |
SHA256 | 哈希摘要(比 MD5 更安全) | 重要参数签名、数据完整性校验 |
HMAC-SHA256 | 带密钥的哈希(防篡改) | 接口请求签名(密钥 + 参数 + 时间戳) |
二、快速集成(Vue3 + Vite 项目)
1. 安装依赖
bash
运行
# 核心库(包含所有常用算法) npm install crypto-js -S # TS 类型(可选,需单独安装) npm install @types/crypto-js -D2. 封装通用加密工具(CRM 首选)
封装统一的加密工具类,避免重复写加密逻辑,同时统一密钥 / 偏移量配置:
typescript
运行
// src/utils/crypto.ts import CryptoJS from 'crypto-js'; // 加密配置(建议从环境变量读取,避免硬编码) const CRYPTO_CONFIG = { // AES 密钥(必须 16/24/32 位,对应 AES-128/AES-192/AES-256) aesKey: import.meta.env.VITE_AES_KEY || 'crm_2026_123456', // AES 偏移量(CBC 模式必填,16 位) aesIv: import.meta.env.VITE_AES_IV || 'crm_iv_12345678', // HMAC 签名密钥 hmacKey: import.meta.env.VITE_HMAC_KEY || 'crm_hmac_2026' }; /** * AES 加密(CBC 模式,兼容后端) * @param data 待加密数据(字符串/对象) * @returns 加密后的 Base64 字符串 */ export const aesEncrypt = (data: string | object): string => { // 统一转为字符串 const dataStr = typeof data === 'object' ? JSON.stringify(data) : data; // 密钥/偏移量转 WordArray const key = CryptoJS.enc.Utf8.parse(CRYPTO_CONFIG.aesKey); const iv = CryptoJS.enc.Utf8.parse(CRYPTO_CONFIG.aesIv); // 加密 const encrypted = CryptoJS.AES.encrypt(dataStr, key, { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 // 后端常用填充方式 }); // 返回 Base64 结果 return encrypted.toString(); }; /** * AES 解密 * @param encryptedStr 加密后的 Base64 字符串 * @returns 解密后的原始数据(字符串/对象) */ export const aesDecrypt = (encryptedStr: string): string | object => { if (!encryptedStr) return ''; const key = CryptoJS.enc.Utf8.parse(CRYPTO_CONFIG.aesKey); const iv = CryptoJS.enc.Utf8.parse(CRYPTO_CONFIG.aesIv); // 解密 const decrypted = CryptoJS.AES.decrypt(encryptedStr, key, { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }); // 转 Utf8 字符串 const resultStr = CryptoJS.enc.Utf8.stringify(decrypted); // 尝试解析为对象(失败则返回字符串) try { return JSON.parse(resultStr); } catch { return resultStr; } }; /** * MD5 哈希(不可逆) * @param data 待哈希数据 * @returns 32 位小写哈希值 */ export const md5 = (data: string | object): string => { const dataStr = typeof data === 'object' ? JSON.stringify(data) : data; return CryptoJS.MD5(dataStr).toString().toLowerCase(); }; /** * SHA256 哈希(不可逆) * @param data 待哈希数据 * @returns 64 位小写哈希值 */ export const sha256 = (data: string | object): string => { const dataStr = typeof data === 'object' ? JSON.stringify(data) : data; return CryptoJS.SHA256(dataStr).toString().toLowerCase(); }; /** * HMAC-SHA256 签名(带密钥,防篡改) * @param data 待签名数据(如接口参数) * @returns 签名结果 */ export const hmacSha256 = (data: string | object): string => { const dataStr = typeof data === 'object' ? JSON.stringify(data) : data; const key = CryptoJS.enc.Utf8.parse(CRYPTO_CONFIG.hmacKey); return CryptoJS.HmacSHA256(dataStr, key).toString().toLowerCase(); }; /** * 接口请求签名(参数+时间戳+随机数) * @param params 接口参数 * @returns { params: 加密参数, sign: 签名, timestamp: 时间戳, nonce: 随机数 } */ export const generateApiSign = (params: object) => { const timestamp = Date.now().toString(); const nonce = Math.random().toString(36).substring(2, 10); // 随机数 // 1. 参数排序(避免参数顺序影响签名) const sortedParams = Object.keys(params).sort().reduce((obj, key) => { obj[key] = params[key]; return obj; }, {} as any); // 2. 拼接签名串:参数JSON + 时间戳 + 随机数 const signStr = JSON.stringify(sortedParams) + timestamp + nonce; // 3. 生成签名 const sign = hmacSha256(signStr); // 4. AES 加密参数 const encryptedParams = aesEncrypt(sortedParams); return { params: encryptedParams, sign, timestamp, nonce }; };三、CRM 实战场景(核心用法)
场景 1:本地存储敏感数据加密
CRM 中用户信息、筛选条件等本地存储需加密,防止明文泄露:
vue
<script setup lang="ts"> import { aesEncrypt, aesDecrypt } from '@/utils/crypto'; // 存储加密后的用户信息 const saveUserInfo = (userInfo) => { const encrypted = aesEncrypt(userInfo); localStorage.setItem('crm-user', encrypted); }; // 读取并解密用户信息 const getUserInfo = () => { const encrypted = localStorage.getItem('crm-user'); return aesDecrypt(encrypted); }; // 使用示例 const userInfo = { id: '123', name: '张三', phone: '13800138000' }; saveUserInfo(userInfo); console.log(getUserInfo()); // { id: '123', name: '张三', phone: '13800138000' } </script>场景 2:登录密码加密(MD5/SHA256)
密码传输前先做哈希加密(不可逆),避免明文传输:
vue
<script setup lang="ts"> import { md5 } from '@/utils/crypto'; import { useUserStore } from '@/stores/user'; const userStore = useUserStore(); const loginForm = ref({ username: '', password: '' }); const handleLogin = async () => { // 密码 MD5 加密(可加盐:md5(loginForm.value.password + 'crm_salt')) const encryptedPwd = md5(loginForm.value.password); // 传给后端 const res = await userStore.login({ username: loginForm.value.username, password: encryptedPwd }); }; </script>场景 3:接口请求签名(防篡改)
CRM 核心接口(如订单提交、客户创建)需签名,防止参数被篡改:
vue
<script setup lang="ts"> import { generateApiSign } from '@/utils/crypto'; import axios from '@/utils/axios'; // 提交订单(核心接口) const submitOrder = async (orderData) => { // 1. 生成签名参数 const { params, sign, timestamp, nonce } = generateApiSign(orderData); // 2. 发送请求 const res = await axios.post('/api/order/submit', { params, // 加密后的参数 sign, // 签名 timestamp, // 时间戳 nonce // 随机数 }); return res.data; }; </script>场景 4:敏感数据脱敏(哈希展示)
客户手机号、身份证号等敏感数据,前端展示哈希后的值(仅用于校验):
vue
<template> <div class="customer-detail"> <p>客户ID:{{ customer.id }}</p> <p>手机号:{{ encryptPhone(customer.phone) }}</p> <p>身份证号:{{ encryptIdCard(customer.idCard) }}</p> </div> </template> <script setup lang="ts"> import { md5 } from '@/utils/crypto'; // 手机号脱敏(保留前3后4,中间哈希) const encryptPhone = (phone) => { if (!phone) return '-'; const prefix = phone.slice(0, 3); const suffix = phone.slice(-4); const middle = md5(phone.slice(3, -4)).slice(0, 4); // 取哈希前4位 return `${prefix}****${suffix}`; }; // 身份证号脱敏 const encryptIdCard = (idCard) => { if (!idCard) return '-'; const prefix = idCard.slice(0, 6); const suffix = idCard.slice(-4); const middle = md5(idCard.slice(6, -4)).slice(0, 8); return `${prefix}********${suffix}`; }; </script>场景 5:Excel 导出密钥加密
CRM 导出 Excel 时,临时密钥加密后拼接在下载链接中:
vue
<script setup lang="ts"> import { aesEncrypt, aesDecrypt } from '@/utils/crypto'; // 生成导出链接 const generateExportUrl = (params) => { // 1. 加密导出参数 const encryptedParams = aesEncrypt(params); // 2. 拼接链接(后端解密后导出) return `${import.meta.env.VITE_BASE_URL}/api/export?params=${encryptedParams}`; }; // 使用示例 const exportOrder = () => { const params = { startTime: '2026-03-01', endTime: '2026-03-20' }; const url = generateExportUrl(params); window.open(url); // 打开下载链接 }; </script>四、核心避坑点
1. AES 密钥 / 偏移量长度问题
- 问题:AES 密钥长度必须是 16/24/32 位(对应 128/192/256 位加密),否则加密失败;
- 解决方案:
- 环境变量配置的密钥严格按长度设置(如 16 位:
VITE_AES_KEY=1234567890123456); - 若后端密钥长度不符,可截取 / 补位:
CryptoJS.enc.Utf8.parse(key).toString().substring(0,16)。
- 环境变量配置的密钥严格按长度设置(如 16 位:
2. 前后端加密模式 / 填充方式不一致
- 问题:前端 CBC 模式 + Pkcs7 填充,后端 ECB 模式 + NoPadding,导致解密失败;
- 解决方案:
- 与后端统一:推荐 CBC 模式 + Pkcs7 填充(通用方案);
- 禁用 ECB 模式(ECB 无偏移量,安全性低)。
3. 中文乱码问题
- 问题:加密 / 解密后中文变成乱码;
- 解决方案:
- 统一使用
CryptoJS.enc.Utf8编码(不要用 Latin1); - 对象加密前先
JSON.stringify,解密后JSON.parse。
- 统一使用
4. MD5 加密后大小写不一致
- 问题:前端 MD5 结果大写,后端小写,导致校验失败;
- 解决方案:统一转为小写(
toString().toLowerCase())。
5. 本地存储加密密钥泄露
- 问题:密钥硬编码在代码中,可通过打包后的 JS 文件反编译获取;
- 解决方案:
- 密钥从后端接口动态获取(首次登录后返回);
- 密钥分片存储(如一部分在代码,一部分在本地存储);
- 生产环境混淆代码(如 Terser 压缩)。
五、总结(CRM 开发最佳实践)
- 封装统一工具:将加密逻辑封装为全局工具函数,避免重复代码,统一密钥 / 算法配置;
- 按需选择算法:
- 本地存储 / 参数加密:用 AES 对称加密;
- 密码 / 签名:用 MD5/SHA256 哈希(不可逆);
- 接口防篡改:用 HMAC-SHA256 带密钥签名;
- 安全注意事项:
- 密钥不要硬编码,优先从环境变量 / 后端接口获取;
- 核心接口必须签名 + 时间戳 + 随机数,防止重放攻击;
- 本地存储敏感数据必须加密,避免 XSS 泄露;
- 前后端对齐:
- 统一加密算法、模式、填充方式、编码格式;
- 测试阶段先做加密 / 解密联调,避免上线后兼容问题。
CryptoJS 是 CRM 系统前端加密的 “标配工具”,只需掌握 AES/MD5/HMAC-SHA256 三种核心算法,就能覆盖 99% 的加密场景;封装后的工具类可直接复用,兼顾安全性与开发效率。