# 存储模块 (TsStorage) **本文引用的文件列表** - [TsStorage.js](file://src/utils/TsStorage.js) - [TsCrypto.js](file://src/utils/TsCrypto.js) - [TsSM4.js](file://src/utils/TsSM4.js) - [TsCommon.js](file://src/utils/TsCommon.js) - [TsGlobalConfig.js](file://src/utils/TsGlobalConfig.js) - [TsHttpUtil.js](file://src/https/TsHttpUtil.js) - [index.js](file://index.js) - [package.json](file://package.json) - [README.md](file://README.md) ## 目录 1. [简介](#简介) 2. [项目结构](#项目结构) 3. [核心组件](#核心组件) 4. [架构总览](#架构总览) 5. [详细组件分析](#详细组件分析) 6. [依赖关系分析](#依赖关系分析) 7. [性能考量](#性能考量) 8. [故障排查指南](#故障排查指南) 9. [结论](#结论) 10. [附录](#附录) ## 简介 本文件为存储模块(TsStorage)的完整数据持久化文档,聚焦于基于浏览器 localStorage 的数据存储机制,涵盖以下主题: - 数据序列化与反序列化流程 - 用户 token 管理 - 加密开关控制与数据生命周期管理 - 与加密模块(SM4/加密器)的集成关系 - 数据安全传输与存储最佳实践 - 完整 API 接口说明(保存、获取、删除等) - 使用示例、数据迁移策略与备份恢复方案 - 浏览器兼容性与存储容量限制 ## 项目结构 该工具包采用“按功能域分层”的组织方式,存储模块位于 utils 目录下,并通过 index.js 汇总导出,供上层业务统一调用。 ```mermaid graph TB subgraph "工具包入口" IDX["index.js"] end subgraph "存储层" TS["TsStorage.js"] TC["TsCommon.js"] end subgraph "加密层" TG["TsGlobalConfig.js"] TSM["TsSM4.js"] TCR["TsCrypto.js"] end subgraph "网络层" TH["TsHttpUtil.js"] end IDX --> TS IDX --> TCR IDX --> TSM IDX --> TG IDX --> TH TH --> TS TH --> TCR TH --> TG TS --> TC TCR --> TSM TCR --> TG ``` 图表来源 - [index.js:1-16](file://index.js#L1-L16) - [TsStorage.js:1-55](file://src/utils/TsStorage.js#L1-L55) - [TsCommon.js:1-98](file://src/utils/TsCommon.js#L1-L98) - [TsCrypto.js:1-34](file://src/utils/TsCrypto.js#L1-L34) - [TsSM4.js:1-456](file://src/utils/TsSM4.js#L1-L456) - [TsGlobalConfig.js:1-34](file://src/utils/TsGlobalConfig.js#L1-L34) - [TsHttpUtil.js:1-171](file://src/https/TsHttpUtil.js#L1-L171) 章节来源 - [index.js:1-16](file://index.js#L1-L16) - [package.json:1-24](file://package.json#L1-L24) ## 核心组件 - 存储模块(TsStorage):封装 localStorage 的读写、token 管理、加密开关管理,提供统一的键值存取接口。 - 加密模块(TsCrypto + TsSM4):基于 SM4 算法的加解密实现,支持 ECB 模式与 Base64 输出。 - 全局配置(TsGlobalConfig):提供 base64Key、前缀、HTTP 参数注入与错误回调等全局配置。 - 通用工具(TsCommon):提供 JSON 解析、空值判断等基础能力。 - 网络工具(TsHttpUtil):在请求中自动注入 token 与加密开关,对响应进行解密与解析。 章节来源 - [TsStorage.js:1-55](file://src/utils/TsStorage.js#L1-L55) - [TsCrypto.js:1-34](file://src/utils/TsCrypto.js#L1-L34) - [TsSM4.js:1-456](file://src/utils/TsSM4.js#L1-L456) - [TsGlobalConfig.js:1-34](file://src/utils/TsGlobalConfig.js#L1-L34) - [TsCommon.js:1-98](file://src/utils/TsCommon.js#L1-L98) - [TsHttpUtil.js:1-171](file://src/https/TsHttpUtil.js#L1-L171) ## 架构总览 存储模块与加密模块的交互路径如下:网络层在发送请求时根据加密开关对请求体进行加密;接收响应时若标记为加密,则进行解密并解析 JSON;存储层负责持久化 token 与加密开关状态。 ```mermaid sequenceDiagram participant App as "应用" participant Http as "TsHttpUtil" participant Store as "TsStorage" participant Crypto as "TsCrypto" participant SM4 as "TsSM4" participant Srv as "后端服务" App->>Store : "saveUserToken(token)" App->>Store : "saveEncryptBody(true/false)" App->>Http : "post(url, data)" Http->>Store : "getEncryptBody()" alt "加密开启" Http->>Crypto : "encrypt(JSON.stringify(data))" Crypto->>SM4 : "encrypt(明文)" SM4-->>Crypto : "密文(Base64)" Crypto-->>Http : "密文" Http->>Srv : "POST { encryptData : 密文 }" else "加密关闭" Http->>Srv : "POST 原始 JSON" end Srv-->>Http : "响应(可能含 encrypt 标记)" alt "响应需解密" Http->>Crypto : "decrypt(密文)" Crypto->>SM4 : "decrypt(密文)" SM4-->>Crypto : "明文" Crypto-->>Http : "明文" Http->>Http : "parseJSON(明文)" end Http-->>App : "{ data, recordsTotal }" ``` 图表来源 - [TsHttpUtil.js:80-91](file://src/https/TsHttpUtil.js#L80-L91) - [TsHttpUtil.js:117-123](file://src/https/TsHttpUtil.js#L117-L123) - [TsStorage.js:17-23](file://src/utils/TsStorage.js#L17-L23) - [TsCrypto.js:19-30](file://src/utils/TsCrypto.js#L19-L30) - [TsSM4.js:338-387](file://src/utils/TsSM4.js#L338-L387) ## 详细组件分析 ### 存储模块(TsStorage) - 功能职责 - 统一的键值存取:将任意值序列化为 JSON 字符串后写入 localStorage。 - 用户 token 管理:提供保存与读取 token 的便捷方法。 - 加密开关管理:保存/读取“是否对请求体进行加密”的布尔标志。 - 安全反序列化:通过通用工具解析 JSON,避免异常导致崩溃。 - 关键实现要点 - 写入:以 {data: value} 的结构写入 localStorage,便于后续统一解析。 - 读取:从 localStorage 读取字符串,交由通用工具解析为对象,再取出 data 字段;未命中或解析失败时返回默认值。 - token 与加密开关:分别以固定键名保存,便于网络层直接读取。 - 错误处理 - 读取失败或 JSON 解析异常时,返回默认值,保证健壮性。 - 性能与复杂度 - 写入/读取均为 O(1),序列化/反序列化为 O(n)(n 为字符串长度)。 - 使用建议 - 对大对象建议在上层进行拆分或压缩,避免 localStorage 膨胀。 - 避免存储敏感信息(如明文密码),优先使用加密开关与服务端保护。 ```mermaid flowchart TD Start(["函数入口"]) --> SaveGet{"save 或 get?"} SaveGet --> |save| Serialize["JSON.stringify({data: value})"] Serialize --> Write["localStorage.setItem(key, 字符串)"] SaveGet --> |get| Read["localStorage.getItem(key)"] Read --> Parse["Common.parseJSON(字符串, {data: 默认值})"] Parse --> Extract["取 data 字段"] Extract --> End(["返回结果"]) ``` 图表来源 - [TsStorage.js:31-43](file://src/utils/TsStorage.js#L31-L43) - [TsCommon.js:34-44](file://src/utils/TsCommon.js#L34-L44) 章节来源 - [TsStorage.js:1-55](file://src/utils/TsStorage.js#L1-L55) - [TsCommon.js:34-44](file://src/utils/TsCommon.js#L34-L44) ### 加密模块(TsCrypto + TsSM4) - 功能职责 - 提供 SM4 加密与解密能力,支持 ECB 模式与 Base64 输出。 - 通过全局配置注入密钥(base64 编码),确保前后端一致。 - 关键实现要点 - 初始化:从全局配置读取 base64Key 并转换为字节数组作为密钥。 - 模式与输出:默认 ECB 模式与 Base64 输出,满足前端直传需求。 - 块处理:按 16 字节块进行填充、轮密钥变换与输出转换。 - 安全性 - ECB 模式适合小数据块加密,但不提供完整性校验;建议仅用于请求体加密场景。 - 密钥来源于全局配置,需确保其安全性与一致性。 - 性能与复杂度 - 单次加/解密为 O(n)(n 为数据长度),块大小固定为 16 字节。 - 使用建议 - 对大文本建议分片处理,避免一次性内存压力。 - 与网络层配合使用,避免在本地重复加密。 ```mermaid classDiagram class TsCrypto { +constructor() +encrypt(content) string +decrypt(base64) string -sm4 : TsSM4 } class TsSM4 { +constructor(config) +encrypt(plaintext) string +decrypt(ciphertext) string -mode : string -cipherType : string -key : Uint8Array -iv : Uint8Array -encryptRoundKeys : Uint32Array -decryptRoundKeys : Uint32Array } TsCrypto --> TsSM4 : "组合" ``` 图表来源 - [TsCrypto.js:5-34](file://src/utils/TsCrypto.js#L5-L34) - [TsSM4.js:96-156](file://src/utils/TsSM4.js#L96-L156) 章节来源 - [TsCrypto.js:1-34](file://src/utils/TsCrypto.js#L1-L34) - [TsSM4.js:1-456](file://src/utils/TsSM4.js#L1-L456) - [TsGlobalConfig.js:19-29](file://src/utils/TsGlobalConfig.js#L19-L29) ### 网络层与存储/加密集成(TsHttpUtil) - 功能职责 - 在请求前根据加密开关对请求体进行加密,并替换为 { encryptData: 密文 }。 - 在响应后根据响应标记进行解密,并解析 JSON。 - 自动注入 token 到请求头。 - 关键流程 - 请求前处理:读取加密开关,若开启则对 data 进行加密;读取 token 注入到 headers。 - 响应后处理:若响应标记为加密,则解密并解析 JSON;否则直接返回 data。 - 错误处理 - 统一错误处理器返回标准化错误对象。 - 性能与复杂度 - 主要开销在 JSON 序列化/反序列化与 SM4 加解密,整体为 O(n)。 - 使用建议 - 通过全局配置统一设置加密开关与密钥,避免分散配置。 ```mermaid sequenceDiagram participant C as "调用方" participant H as "TsHttpUtil" participant S as "TsStorage" participant E as "TsCrypto" participant M as "TsSM4" participant R as "后端" C->>H : "post(url, {data})" H->>S : "getEncryptBody()" alt "加密开启" H->>E : "encrypt(JSON.stringify(data))" E->>M : "encrypt(明文)" M-->>E : "密文" E-->>H : "密文" H->>R : "POST { encryptData : 密文 }" else "加密关闭" H->>R : "POST { data }" end R-->>H : "响应(可能含 encrypt 标记)" alt "响应需解密" H->>E : "decrypt(密文)" E->>M : "decrypt(密文)" M-->>E : "明文" E-->>H : "明文" H->>H : "parseJSON(明文)" end H-->>C : "{ data, recordsTotal }" ``` 图表来源 - [TsHttpUtil.js:50-91](file://src/https/TsHttpUtil.js#L50-L91) - [TsHttpUtil.js:117-123](file://src/https/TsHttpUtil.js#L117-L123) - [TsStorage.js:17-23](file://src/utils/TsStorage.js#L17-L23) - [TsCrypto.js:19-30](file://src/utils/TsCrypto.js#L19-L30) - [TsSM4.js:338-387](file://src/utils/TsSM4.js#L338-L387) 章节来源 - [TsHttpUtil.js:1-171](file://src/https/TsHttpUtil.js#L1-L171) - [TsStorage.js:1-55](file://src/utils/TsStorage.js#L1-L55) - [TsCrypto.js:1-34](file://src/utils/TsCrypto.js#L1-L34) - [TsSM4.js:1-456](file://src/utils/TsSM4.js#L1-L456) ### API 接口文档(存储模块) - 保存用户 token - 方法:saveUserToken(token) - 作用:将 token 以固定键名保存至 localStorage - 返回:无 - 参考:[TsStorage.js:9-11](file://src/utils/TsStorage.js#L9-L11) - 获取用户 token - 方法:getUserToken() - 作用:从 localStorage 读取 token,未命中返回空字符串 - 返回:字符串 - 参考:[TsStorage.js:13-15](file://src/utils/TsStorage.js#L13-L15) - 开启/关闭请求体加密 - 方法:saveEncryptBody(bool) - 作用:保存布尔值以控制请求体是否加密 - 返回:无 - 参考:[TsStorage.js:17-19](file://src/utils/TsStorage.js#L17-L19) - 读取加密开关 - 方法:getEncryptBody() - 作用:读取加密开关,未命中返回默认 true - 返回:布尔值 - 参考:[TsStorage.js:21-23](file://src/utils/TsStorage.js#L21-L23) - 通用保存 - 方法:save(key, value) - 作用:将任意值序列化后保存至 localStorage - 返回:无 - 参考:[TsStorage.js:31-33](file://src/utils/TsStorage.js#L31-L33) - 通用获取 - 方法:get(key, def) - 作用:从 localStorage 读取并解析 JSON,返回 data 字段 - 返回:任意类型(默认值为传入 def) - 参考:[TsStorage.js:41-43](file://src/utils/TsStorage.js#L41-L43) 章节来源 - [TsStorage.js:1-55](file://src/utils/TsStorage.js#L1-L55) ### 数据序列化与反序列化 - 序列化 - 将任意值包装为 {data: value},再通过 JSON.stringify 转为字符串,写入 localStorage。 - 优点:统一结构,便于后续解析;可扩展字段(如版本号)。 - 参考:[TsStorage.js:31-33](file://src/utils/TsStorage.js#L31-L33) - 反序列化 - 从 localStorage 读取字符串,使用通用工具解析为对象,取 data 字段作为实际值。 - 若解析失败或未命中,返回默认值(def)。 - 参考:[TsStorage.js:41-43](file://src/utils/TsStorage.js#L41-L43), [TsCommon.js:34-44](file://src/utils/TsCommon.js#L34-L44) 章节来源 - [TsStorage.js:31-43](file://src/utils/TsStorage.js#L31-L43) - [TsCommon.js:34-44](file://src/utils/TsCommon.js#L34-L44) ### 用户 token 管理 - 存储位置:固定键名为 token - 读写接口:saveUserToken/getUserToken - 使用场景:网络层在请求头中注入 token,实现鉴权 - 参考:[TsStorage.js:9-15](file://src/utils/TsStorage.js#L9-L15), [TsHttpUtil.js:109-111](file://src/https/TsHttpUtil.js#L109-L111) 章节来源 - [TsStorage.js:1-55](file://src/utils/TsStorage.js#L1-L55) - [TsHttpUtil.js:109-111](file://src/https/TsHttpUtil.js#L109-L111) ### 加密开关控制与数据生命周期 - 加密开关 - 保存键名:encrypt_body - 默认值:true(开启加密) - 作用:控制网络层是否对请求体进行加密 - 参考:[TsStorage.js:17-23](file://src/utils/TsStorage.js#L17-L23), [TsHttpUtil.js:82-86](file://src/https/TsHttpUtil.js#L82-L86) - 生命周期 - 保存:saveEncryptBody(bool) - 读取:getEncryptBody(),未命中返回 true - 删除:localStorage.removeItem("encrypt_body") - 参考:[TsStorage.js:17-23](file://src/utils/TsStorage.js#L17-L23) 章节来源 - [TsStorage.js:1-55](file://src/utils/TsStorage.js#L1-L55) - [TsHttpUtil.js:80-91](file://src/https/TsHttpUtil.js#L80-L91) ### 存储模块与加密模块的集成关系 - 网络层在请求前读取加密开关,决定是否对 data 进行加密。 - 响应后若标记为加密,网络层进行解密并解析 JSON。 - 存储层负责持久化 token 与加密开关,形成闭环。 - 参考:[TsHttpUtil.js:80-91](file://src/https/TsHttpUtil.js#L80-L91), [TsHttpUtil.js:117-123](file://src/https/TsHttpUtil.js#L117-L123), [TsStorage.js:17-23](file://src/utils/TsStorage.js#L17-L23) 章节来源 - [TsHttpUtil.js:1-171](file://src/https/TsHttpUtil.js#L1-L171) - [TsStorage.js:1-55](file://src/utils/TsStorage.js#L1-L55) ### 数据安全传输与存储最佳实践 - 传输安全 - 使用加密开关对请求体进行加密,避免明文在网络中传输。 - 仅对必要字段加密,避免过度加密影响性能。 - 参考:[TsHttpUtil.js:80-91](file://src/https/TsHttpUtil.js#L80-L91) - 存储安全 - 不在 localStorage 中存储敏感明文(如密码、私钥)。 - 使用加密开关保护本地缓存的敏感数据。 - 参考:[TsStorage.js:31-43](file://src/utils/TsStorage.js#L31-L43) - 配置安全 - 密钥来源于全局配置,需确保其保密性与一致性。 - 参考:[TsGlobalConfig.js:19-29](file://src/utils/TsGlobalConfig.js#L19-L29), [TsCrypto.js:7-13](file://src/utils/TsCrypto.js#L7-L13) 章节来源 - [TsHttpUtil.js:1-171](file://src/https/TsHttpUtil.js#L1-L171) - [TsStorage.js:1-55](file://src/utils/TsStorage.js#L1-L55) - [TsGlobalConfig.js:1-34](file://src/utils/TsGlobalConfig.js#L1-L34) - [TsCrypto.js:1-34](file://src/utils/TsCrypto.js#L1-L34) ### 使用示例 - 保存与读取普通数据 - 参考:[README.md:28-41](file://README.md#L28-L41) - 设置加密开关 - 参考:[README.md:12-26](file://README.md#L12-L26) - 保存用户 token - 参考:[TsStorage.js:9-15](file://src/utils/TsStorage.js#L9-L15) 章节来源 - [README.md:1-43](file://README.md#L1-L43) - [TsStorage.js:1-55](file://src/utils/TsStorage.js#L1-L55) ### 数据迁移策略与备份恢复 - 迁移策略 - 版本升级时,可在存储层增加版本字段,读取时进行兼容性处理。 - 对历史数据进行批量重写(如重新序列化),确保新结构可用。 - 参考:[TsStorage.js:31-43](file://src/utils/TsStorage.js#L31-L43) - 备份与恢复 - 备份:读取所有键值并导出为 JSON 文件。 - 恢复:导入 JSON 后逐条写入 localStorage。 - 注意:避免在恢复过程中覆盖当前用户数据,建议先导入到临时键名,再合并。 - 参考:[TsStorage.js:31-43](file://src/utils/TsStorage.js#L31-L43) 章节来源 - [TsStorage.js:1-55](file://src/utils/TsStorage.js#L1-L55) ### 浏览器兼容性与存储容量限制 - 浏览器兼容性 - localStorage 在现代浏览器中广泛支持;在无痕模式或受限环境下可能不可用。 - 参考:[TsStorage.js:31-43](file://src/utils/TsStorage.js#L31-L43) - 存储容量限制 - localStorage 通常为 5–10 MB(因浏览器而异),超出会抛出异常。 - 建议:对大对象进行分片或压缩,避免频繁写入导致溢出。 - 参考:[TsStorage.js:31-43](file://src/utils/TsStorage.js#L31-L43) 章节来源 - [TsStorage.js:1-55](file://src/utils/TsStorage.js#L1-L55) ## 依赖关系分析 - 模块耦合 - TsHttpUtil 依赖 TsStorage、TsCrypto、TsGlobalConfig,形成“网络层-存储层-加密层-配置层”的链路。 - TsStorage 依赖 TsCommon,用于安全解析 JSON。 - TsCrypto 依赖 TsSM4 与全局配置,提供加解密能力。 - 外部依赖 - base64-js:Base64 编解码与字节数组互转。 - umi-request:HTTP 请求封装与错误处理。 - 可能的循环依赖 - 当前模块间无循环依赖,结构清晰。 ```mermaid graph LR Http["TsHttpUtil"] --> Store["TsStorage"] Http --> Crypto["TsCrypto"] Http --> Global["TsGlobalConfig"] Store --> Common["TsCommon"] Crypto --> SM4["TsSM4"] Crypto --> Global ``` 图表来源 - [index.js:1-16](file://index.js#L1-L16) - [TsHttpUtil.js:1-6](file://src/https/TsHttpUtil.js#L1-L6) - [TsStorage.js:1](file://src/utils/TsStorage.js#L1) - [TsCrypto.js:1-3](file://src/utils/TsCrypto.js#L1-L3) - [TsSM4.js:1](file://src/utils/TsSM4.js#L1) - [TsGlobalConfig.js:1-3](file://src/utils/TsGlobalConfig.js#L1-L3) - [TsCommon.js:1](file://src/utils/TsCommon.js#L1) 章节来源 - [index.js:1-16](file://index.js#L1-L16) - [package.json:19-22](file://package.json#L19-L22) ## 性能考量 - 序列化/反序列化成本:O(n),n 为字符串长度;建议对大对象进行分片或压缩。 - 加解密成本:SM4 单次加/解密 O(n),注意避免对频繁小数据进行重复加密。 - I/O 成本:localStorage 读写为 O(1),但频繁写入可能导致主线程阻塞,建议批量写入或延迟写入。 - 建议:在业务层对热点数据进行缓存,减少重复序列化与写入。 ## 故障排查指南 - 读取不到数据 - 检查键名是否正确;确认是否被覆盖或删除。 - 参考:[TsStorage.js:41-43](file://src/utils/TsStorage.js#L41-L43) - JSON 解析失败 - 检查存储内容是否为合法 JSON;确认是否被其他模块篡改。 - 参考:[TsCommon.js:34-44](file://src/utils/TsCommon.js#L34-L44) - 加密/解密异常 - 检查密钥是否正确;确认 ECB 模式与 Base64 输出是否匹配。 - 参考:[TsCrypto.js:7-13](file://src/utils/TsCrypto.js#L7-L13), [TsSM4.js:338-387](file://src/utils/TsSM4.js#L338-L387) - 请求未加密 - 检查加密开关是否开启;确认网络层是否正确读取开关。 - 参考:[TsHttpUtil.js:80-91](file://src/https/TsHttpUtil.js#L80-L91), [TsStorage.js:17-23](file://src/utils/TsStorage.js#L17-L23) 章节来源 - [TsStorage.js:1-55](file://src/utils/TsStorage.js#L1-L55) - [TsCommon.js:1-98](file://src/utils/TsCommon.js#L1-L98) - [TsCrypto.js:1-34](file://src/utils/TsCrypto.js#L1-L34) - [TsSM4.js:1-456](file://src/utils/TsSM4.js#L1-L456) - [TsHttpUtil.js:1-171](file://src/https/TsHttpUtil.js#L1-L171) ## 结论 TsStorage 通过统一的序列化/反序列化与键值管理,为应用提供了简洁可靠的本地存储能力;结合加密模块与网络层,实现了从传输到存储的端到端安全方案。建议在生产环境中: - 明确敏感数据边界,合理启用加密开关; - 控制存储体积,避免 localStorage 溢出; - 在升级时做好版本兼容与迁移策略; - 严格管理密钥与配置,确保一致性与安全性。 ## 附录 - 入口导出 - index.js 汇总导出各模块,便于统一引入。 - 参考:[index.js:8-15](file://index.js#L8-L15) - 依赖清单 - base64-js、umi-request 等外部依赖。 - 参考:[package.json:19-22](file://package.json#L19-L22) 章节来源 - [index.js:1-16](file://index.js#L1-L16) - [package.json:1-24](file://package.json#L1-L24)