# 实现方案 > 时间戳:2026-04-20-10-14-09 > 对应需求分析:`需求分析-2026-04-20-10-14-09.md` --- ## 1. 方案概述 在不改变 `storage.get/set` 同步调用签名的前提下,将 `systemSettings` 的底层加密从 XOR 迁移至 Web Crypto API AES-GCM,密钥与 API Key、授权哈希全部移入构建阶段环境变量。应用启动时异步初始化存储模块,预解密 `systemSettings` 到内存缓存,后续同步读写均走缓存。旧 XOR 密文自动识别并迁移。UserManage.tsx 中的明文授权常量替换为 SHA-256 环境变量哈希比对,同时以最小代价替换原生 alert/confirm 为内联状态提示。 --- ## 2. 详细改动清单 ### 2.1 文件:src/utils/storage.ts | 行号范围 | 改动类型 | 说明 | |----------|----------|------| | 1-6 | 删除 | 移除 `API_KEY_CODES` 数组与 `getDefaultApiKey()` 的旧实现 | | 8 | 修改 | `CRYPTO_KEY` 改为从 `process.env.STORAGE_KEY` 读取,并更名为 `LEGACY_CRYPTO_KEY` | | 10-25 | 保留 | 旧 `xorEncrypt` / `xorDecrypt` 保留用于向后兼容读取 | | 新增 | 新增 | 引入 Web Crypto API:`importCryptoKey`、`aesEncrypt`、`aesDecrypt` | | 新增 | 新增 | `AES_GCM_PREFIX = 'ag:'` 用于识别新格式密文 | | 新增 | 新增 | `systemSettingsCache`、`initPromise` 等模块级状态 | | 新增 | 新增 | 异步 `init()` 函数:导入密钥、预加载 systemSettings、自动迁移旧数据 | | 27-43 | 修改 | `get()` 对 `systemSettings` 改为从内存缓存返回 | | 45-55 | 修改 | `set()` 对 `systemSettings` 改为更新缓存并触发异步 `persistSystemSettings()` | | 新增 | 新增 | `sha256(text)` 工具函数(Web Crypto API)供 UserManage.tsx 使用 | **关键代码逻辑**: ```typescript // AES-GCM 密文格式: ag:: const AES_GCM_PREFIX = 'ag:'; async function init() { const keyData = process.env.STORAGE_KEY || ''; if (!keyData) throw new Error('STORAGE_KEY not configured in build env'); cryptoKey = await crypto.subtle.importKey( 'raw', new TextEncoder().encode(keyData.padEnd(32, '0').slice(0, 32)), { name: 'AES-GCM' }, false, ['encrypt', 'decrypt'] ); const raw = localStorage.getItem('systemSettings'); if (raw) { if (raw.startsWith(AES_GCM_PREFIX)) { systemSettingsCache = await aesDecrypt(raw); } else { try { systemSettingsCache = JSON.parse(raw); } catch { systemSettingsCache = JSON.parse(xorDecrypt(raw, LEGACY_KEY)); } await persistSystemSettings(); // 静默迁移 } } } ``` ### 2.2 文件:src/main.tsx | 行号范围 | 改动类型 | 说明 | |----------|----------|------| | 4-10 | 修改 | 包装为异步 `bootstrap()`,先 `await storage.init()` 再 render App | ```typescript import { storage } from './utils/storage'; async function bootstrap() { await storage.init(); createRoot(document.getElementById('root')!).render( ); } bootstrap(); ``` ### 2.3 文件:vite.config.ts | 行号范围 | 改动类型 | 说明 | |----------|----------|------| | 10-12 | 修改 | 在 `define` 中新增 4 个环境变量注入 | ```typescript define: { 'process.env.GEMINI_API_KEY': JSON.stringify(env.GEMINI_API_KEY), 'process.env.STORAGE_KEY': JSON.stringify(env.STORAGE_KEY), 'process.env.DEFAULT_API_KEY': JSON.stringify(env.DEFAULT_API_KEY), 'process.env.ADMIN_AUTH_HASH': JSON.stringify(env.ADMIN_AUTH_HASH), 'process.env.ADMIN_AUTH_SALT': JSON.stringify(env.ADMIN_AUTH_SALT), }, ``` ### 2.4 文件:.env.example | 行号范围 | 改动类型 | 说明 | |----------|----------|------| | 新增 | 新增 | 追加 4 个必需环境变量模板 | ```bash # 本地存储 AES-GCM 加密密钥(建议 32 位随机字符串) STORAGE_KEY="YourRandom32CharKeyHere!!!" # 默认 AI API Key(替代原 API_KEY_CODES 硬编码) DEFAULT_API_KEY="your-api-key-here" # 禁用管理员操作的授权码 SHA-256 哈希值(hex 小写) ADMIN_AUTH_HASH="your-sha256-hash-here" # 授权码哈希盐值 ADMIN_AUTH_SALT="your-random-salt-here" ``` ### 2.5 文件:src/pages/UserManage.tsx | 行号范围 | 改动类型 | 说明 | |----------|----------|------| | 8 | 删除 | 移除 `ADMIN_DISABLE_AUTH_KEY = 'DISABLE_ADMIN_2024'` | | 新增 | 新增 | 从环境变量读取 `ADMIN_AUTH_HASH` 和 `ADMIN_AUTH_SALT` | | 29 | 修改 | `authKey` 保持,但比对逻辑改为 SHA-256 | | 新增 | 新增 | 增加 `errorMessage`、`confirmDialog` 状态替换 alert/confirm | | 103-116 | 修改 | `handleDelete` 改为触发确认对话框状态,移除 `window.confirm` | | 118-154 | 修改 | `handleEdit` 中 alert 改为设置 `errorMessage` | | 185-324 | 修改 | `handleSubmit` 改为 async,所有 alert 改为 `errorMessage`,授权比对改为 `await sha256(authKey + salt)` | | 新增 JSX | 新增 | 表单顶部增加错误提示条;新增轻量确认对话框覆盖层 | --- ## 3. 关键设计决策 | 决策点 | 选择方案 | 备选方案 | 不选备选的原因 | |--------|----------|----------|----------------| | systemSettings 异步加密的同步接口兼容 | 模块初始化时预解密到内存缓存,get/set 保持同步 | 将 storage.get/set 全局改为 async | 调用点超过 50 处,改动面过大,风险不可控 | | AES-GCM 密钥长度处理 | 将环境变量字符串 padEnd 到 32 字节后截断 | 要求用户必须提供 32 字节 | 提升用户体验,减少配置门槛 | | 旧数据迁移时机 | init() 中自动检测并静默迁移 | 提供手动迁移脚本 | 纯前端无脚本执行环境,自动迁移对用户透明 | | SHA-256 实现选择 | Web Crypto API(已在 storage.ts 提供工具函数) | crypto-js 同步计算 | 统一使用 Web Crypto API,减少额外依赖引用 | | alert/confirm 替换策略 | 内联状态提示条 + 轻量确认状态 | 抽取全局 Modal 组件 | 项目中无现成 Modal,本次以最小代价消除明文暴露和原生弹窗 | --- ## 4. 与现有规范的兼容性检查 - [ ] contentEditable 编辑器规范(5.1) — 本次不涉及编辑器 - [x] 自动保存与草稿机制(5.2) — storage 接口签名不变,ReportEditor 自动保存逻辑无影响 - [ ] 图片与视频处理(5.3) — 本次不涉及 - [ ] 智能字段与动态表单(5.4) — 本次不涉及 - [ ] AI 功能开发规范(5.5) — `getDefaultApiKey()` 返回值不变,调用方无感知 - [x] 数据迁移与类型安全(5.6) — 旧 XOR 密文自动迁移,使用 `(obj as any).oldField` 安全访问思路已体现在旧格式兼容分支 - [x] UI/UX 通用规范(5.7) — UserManage.tsx 中原生 alert/confirm 全部替换为自定义状态提示 --- ## 5. 回滚策略 若构建失败或运行异常: ```bash git reset --hard HEAD~1 ``` 或手动还原: 1. 恢复 `src/utils/storage.ts` 中的 XOR 逻辑与 `API_KEY_CODES` 2. 恢复 `src/pages/UserManage.tsx` 中的 `ADMIN_DISABLE_AUTH_KEY` 3. 恢复 `src/main.tsx` 为同步 render 4. 还原 `vite.config.ts` 的 define 配置 --- ## 6. 预估工作量 | 阶段 | 预估时间 | |------|----------| | 代码修改 | 25 min | | 测试验证 | 15 min | | 文档更新 | 10 min | --- **状态**:✅ 用户已豁免人工审核确认