From 7b64cedf2c2086f18a9ffc6287cc098138d91343 Mon Sep 17 00:00:00 2001 From: ZLY Date: Thu, 20 Nov 2025 10:22:10 +0800 Subject: [PATCH] =?UTF-8?q?feat(login):=20=E5=8A=A0=E5=AF=86=E5=AD=98?= =?UTF-8?q?=E5=82=A8=E7=99=BB=E5=BD=95=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 引入 crypto-js 库实现 AES 加密功能 - 新增 utils/crypto.ts 工具文件,提供加密解密方法 - 修改登录表单,对记住密码功能中的用户名和密码进行加密存储 - 更新 package.json 和 lock 文件,添加相关依赖和类型定义 - 优化解密逻辑,增加错误处理和空值判断 - 使用加密方法替换原有的 JSON 字符串存储方式 --- package.json | 2 ++ pnpm-lock.yaml | 16 ++++++++++ src/pages/login/form.tsx | 11 ++++--- src/utils/crypto.ts | 69 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 94 insertions(+), 4 deletions(-) create mode 100644 src/utils/crypto.ts diff --git a/package.json b/package.json index 52a9054..805b6a3 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "copy-to-clipboard": "^3.3.1", "cron-parser": "^5.3.1", "cronstrue": "^3.2.0", + "crypto-js": "^4.2.0", "dayjs": "^1.11.13", "echarts": "^5.6.0", "echarts-for-react": "^3.0.2", @@ -55,6 +56,7 @@ }, "devDependencies": { "@svgr/webpack": "^5.5.0", + "@types/crypto-js": "^4.2.2", "@types/node": "16.11.7", "@types/react": "17.0.2", "@types/react-dom": "^17.0.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 086f637..09c7f38 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -71,6 +71,9 @@ importers: cronstrue: specifier: ^3.2.0 version: 3.2.0 + crypto-js: + specifier: ^4.2.0 + version: 4.2.0 dayjs: specifier: ^1.11.13 version: 1.11.13 @@ -126,6 +129,9 @@ importers: '@svgr/webpack': specifier: ^5.5.0 version: 5.5.0 + '@types/crypto-js': + specifier: ^4.2.2 + version: 4.2.2 '@types/node': specifier: 16.11.7 version: 16.11.7 @@ -1478,6 +1484,9 @@ packages: '@types/cookie@0.3.3': resolution: {integrity: sha512-LKVP3cgXBT9RYj+t+9FDKwS5tdI+rPBXaNSkma7hvqy35lc7mAokC2zsqWJH0LaqIt3B962nuYI77hsJoT1gow==} + '@types/crypto-js@4.2.2': + resolution: {integrity: sha512-sDOLlVbHhXpAUAL0YHDUUwDZf3iN4Bwi4W6a0W0b+QcAezUbRtH4FVb+9J4h+XFPW7l/gQ9F8qC7P+Ec4k8QVQ==} + '@types/d3-color@3.1.3': resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==} @@ -2180,6 +2189,9 @@ packages: crypto-browserify@3.12.0: resolution: {integrity: sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==} + crypto-js@4.2.0: + resolution: {integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==} + css-functions-list@3.2.3: resolution: {integrity: sha512-IQOkD3hbR5KrN93MtcYuad6YPuTSUhntLHDuLEbFWE+ff2/XSZNdZG+LcbbIW5AXKg/WFIfYItIzVoHngHXZzA==} engines: {node: '>=12 || >=16'} @@ -7068,6 +7080,8 @@ snapshots: '@types/cookie@0.3.3': {} + '@types/crypto-js@4.2.2': {} + '@types/d3-color@3.1.3': {} '@types/d3-drag@3.0.7': @@ -7968,6 +7982,8 @@ snapshots: randombytes: 2.1.0 randomfill: 1.0.4 + crypto-js@4.2.0: {} + css-functions-list@3.2.3: {} css-select-base-adapter@0.1.1: {} diff --git a/src/pages/login/form.tsx b/src/pages/login/form.tsx index 9f0a9d8..abfde07 100644 --- a/src/pages/login/form.tsx +++ b/src/pages/login/form.tsx @@ -14,6 +14,7 @@ import useStorage from '@/utils/useStorage'; import useLocale from '@/utils/useLocale'; import locale from './locale'; import styles from './style/index.module.less'; +import { encryptLoginParams, decryptLoginParams } from '@/utils/crypto'; export default function LoginForm() { const formRef = useRef(); @@ -29,7 +30,7 @@ export default function LoginForm() { function afterLoginSuccess(params, token?: string) { // 记住密码 if (rememberPassword) { - setLoginParams(JSON.stringify(params)); + setLoginParams(encryptLoginParams(params)); } else { removeLoginParams(); @@ -63,13 +64,15 @@ export default function LoginForm() { }); } - // 读取 localStorage,设置初始值 + // 读取 localStorage,设置初始值 useEffect(() => { const rememberPassword = !!loginParams; setRememberPassword(rememberPassword); if (formRef.current && rememberPassword) { - const parseParams = JSON.parse(loginParams); - formRef.current.setFieldsValue(parseParams); + const parseParams = decryptLoginParams(loginParams); + if (parseParams) { + formRef.current.setFieldsValue(parseParams); + } } }, [loginParams]); diff --git a/src/utils/crypto.ts b/src/utils/crypto.ts new file mode 100644 index 0000000..fe27db2 --- /dev/null +++ b/src/utils/crypto.ts @@ -0,0 +1,69 @@ +import CryptoJS from 'crypto-js'; + +// 加密密钥 - 建议从环境变量读取,这里使用固定密钥作为示例 +const SECRET_KEY = 'flow-platform-2025-secret-key'; + +/** + * AES 加密 + * @param data 需要加密的数据(对象会被序列化为 JSON) + * @returns 加密后的字符串 + */ +export function encrypt(data: any): string { + try { + const jsonString = typeof data === 'string' ? data : JSON.stringify(data); + const encrypted = CryptoJS.AES.encrypt(jsonString, SECRET_KEY).toString(); + return encrypted; + } catch (error) { + console.error('加密失败:', error); + throw error; + } +} + +/** + * AES 解密 + * @param encryptedData 加密的字符串 + * @returns 解密后的数据(如果是 JSON 对象会自动解析) + */ +export function decrypt(encryptedData: string): any { + try { + const bytes = CryptoJS.AES.decrypt(encryptedData, SECRET_KEY); + const decryptedString = bytes.toString(CryptoJS.enc.Utf8); + + if (!decryptedString) { + throw new Error('解密失败:无法解析数据'); + } + + // 尝试解析为 JSON,如果失败则返回原始字符串 + try { + return JSON.parse(decryptedString); + } catch { + return decryptedString; + } + } catch (error) { + console.error('解密失败:', error); + throw error; + } +} + +/** + * 加密登录参数(用户名和密码) + * @param params 包含 username 和 password 的对象 + * @returns 加密后的字符串 + */ +export function encryptLoginParams(params: { username: string; password: string }): string { + return encrypt(params); +} + +/** + * 解密登录参数 + * @param encryptedParams 加密的登录参数字符串 + * @returns 解密后的对象,包含 username 和 password + */ +export function decryptLoginParams(encryptedParams: string): { username: string; password: string } | null { + try { + return decrypt(encryptedParams); + } catch (error) { + console.error('解密登录参数失败:', error); + return null; + } +}