3.8 KiB
3.8 KiB
经验记录
本文档用于沉淀项目修改过程中遇到的关键问题及解决方案。 每次执行工作流 Step 5 后,按四段式追加新记录。 执行修改前必须阅读本文档,避免重复踩坑。
记录格式
## [{Year}-{Mon}-{Day}-{Hour}-{Min}-{Sec}] 问题简述
### A. 具体问题
(描述遇到的现象或错误)
### B. 产生问题原因
(根因分析)
### C. 解决问题方案
(具体的解决步骤或代码改动)
### D. 后续如何避免问题
(预防措施、规范建议、检查清单项)
历史记录
[2026-04-20-10-14-09] Web Crypto API 异步性与 storage 同步接口冲突
A. 具体问题
需求要求将 systemSettings 的加密从 XOR 升级为 Web Crypto API AES-GCM,同时维持 storage.get/set 的同步调用签名。但 crypto.subtle 的所有加解密操作均为异步 Promise,若直接将 storage.get/set 改为 async,将导致 ReportEditor.tsx、Login.tsx 等数十处调用点需要同步改为 await,改动面极大且风险不可控。
B. 产生问题原因
Web Crypto API 是浏览器原生标准,设计为全异步 API,无法像 crypto-js 那样提供同步调用。而项目早期设计时 storage 模块全部为同步接口,未预留异步扩展点。
C. 解决问题方案
采用"启动时预解密 + 内存缓存"的折中方案:
- 在
storage.ts中新增异步init()函数,在应用启动(main.tsx中await storage.init())时预解密systemSettings到模块级内存缓存systemSettingsCache。 storage.get('systemSettings')改为从缓存同步返回,storage.set('systemSettings')改为更新缓存并触发异步持久化(不阻塞调用方)。- 其他 key 的读写保持原样,完全不受影响。
- 该方案无需修改任何业务组件中
storage.get('systemSettings')的调用方式。
D. 后续如何避免问题
- 若未来再次遇到"必须引入异步存储操作"的需求,优先评估能否用"启动预加载 + 内存缓存"模式规避接口签名变更。
- 若必须改异步接口,应将影响范围限制在单一 key(如新增
getAsync/setAsync),而非全局改动storage.get/set。 - 在需求分析阶段增加"接口兼容性影响评估"子项,提前识别同步/异步冲突。
[2026-04-20-10-14-09] 旧数据兼容密钥与"消除硬编码"验收标准冲突
A. 具体问题
安全加固的验收标准要求"构建产物中不再出现任何硬编码密钥字符串"。但实现 AES-GCM 迁移时,必须保留旧 XOR 密钥 MedicalReportSys2024 才能解密历史 localStorage 中的 systemSettings 数据。首次修改后构建产物扫描发现该字符串仍然存在。
B. 产生问题原因
数据迁移需求(兼容旧 XOR 密文)与安全需求(消除硬编码密钥)表面上互斥。根源在于旧密钥是以明文常量形式写死在源码中的,构建产物必然包含该字符串。
C. 解决问题方案
将旧 XOR 密钥同样移入构建阶段环境变量 LEGACY_STORAGE_KEY,通过 vite.config.ts 的 define 注入:
- 构建产物中不再包含固定的
"MedicalReportSys2024"。 - 若部署方需要兼容旧数据,在构建时配置
LEGACY_STORAGE_KEY="MedicalReportSys2024"即可正常迁移。 - 若部署方不需要兼容(全新部署),留空即可,旧数据将以 JSON.parse 失败处理,回退到 fallback。
D. 后续如何避免问题
- 任何涉及历史数据迁移的安全重构,必须在方案设计阶段同步考虑"旧密钥/旧算法常量是否需要保留"。
- 检查清单:若验收标准包含"构建产物无硬编码敏感字符串",则所有历史兼容所需的常量也必须环境变量化。
.env.example中应显式标注LEGACY_STORAGE_KEY的用途("Former XOR encryption key"),避免运维人员误删。