7.1 KiB
7.1 KiB
实现方案
时间戳: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 使用 |
关键代码逻辑:
// AES-GCM 密文格式: ag:<base64(iv)>:<base64(ciphertext)>
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 |
import { storage } from './utils/storage';
async function bootstrap() {
await storage.init();
createRoot(document.getElementById('root')!).render(
<StrictMode><App /></StrictMode>
);
}
bootstrap();
2.3 文件:vite.config.ts
| 行号范围 | 改动类型 | 说明 |
|---|---|---|
| 10-12 | 修改 | 在 define 中新增 4 个环境变量注入 |
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 个必需环境变量模板 |
# 本地存储 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) — 本次不涉及编辑器
- 自动保存与草稿机制(5.2) — storage 接口签名不变,ReportEditor 自动保存逻辑无影响
- 图片与视频处理(5.3) — 本次不涉及
- 智能字段与动态表单(5.4) — 本次不涉及
- AI 功能开发规范(5.5) —
getDefaultApiKey()返回值不变,调用方无感知 - 数据迁移与类型安全(5.6) — 旧 XOR 密文自动迁移,使用
(obj as any).oldField安全访问思路已体现在旧格式兼容分支 - UI/UX 通用规范(5.7) — UserManage.tsx 中原生 alert/confirm 全部替换为自定义状态提示
5. 回滚策略
若构建失败或运行异常:
git reset --hard HEAD~1
或手动还原:
- 恢复
src/utils/storage.ts中的 XOR 逻辑与API_KEY_CODES - 恢复
src/pages/UserManage.tsx中的ADMIN_DISABLE_AUTH_KEY - 恢复
src/main.tsx为同步 render - 还原
vite.config.ts的 define 配置
6. 预估工作量
| 阶段 | 预估时间 |
|---|---|
| 代码修改 | 25 min |
| 测试验证 | 15 min |
| 文档更新 | 10 min |
状态:✅ 用户已豁免人工审核确认