Pinia需通过插件实现加密持久化:初始化时解密加载、变更时加密保存,推荐用pinia-plugin-persistedstate替换storage对象,结合Web Crypto API或crypto-js按安全等级选AES-GCM/AES-CBC,密钥禁硬编码,字段级控制加密范围并做好SSR守卫。

Pinia 本身不提供加密能力,持久化也默认明文存 localStorage。要自定义加密解密,核心是在状态写入存储前加密、读取时解密,整个过程通过插件介入序列化/反序列化环节,对 Store 逻辑完全透明。
选合适的加密方案
根据安全等级选择:
-
中低敏感场景(如用户偏好、主题设置):用
crypto-js的 AES-CBC 模式,搭配固定 IV,实现简单、兼容性好 -
高安全要求(如 token、部分用户信息):优先用浏览器原生
Web Crypto API的 AES-GCM,自带认证,防篡改,但需处理 Promise 和兼容性兜底 - 密钥不能硬编码在前端代码里,建议从环境变量注入,或结合后端动态下发(需额外鉴权逻辑)
手动封装加密插件
不依赖第三方加密持久化插件,自己写一个轻量插件更可控:
- 监听 store 初始化:从 localStorage 读出密文,调用
decrypt()还原后用store.$patch()合并到初始状态 - 监听状态变更:用
store.$subscribe()捕获变化,提取需持久化的字段(可配合paths过滤),加密后写入 localStorage - 加解密函数里统一处理 JSON 序列化/反序列化,避免解密后解析失败——失败时清除对应 key,防止下次反复报错
- 推荐为每个 store 加唯一前缀(如
encrypted_user),避免多 store 冲突
用 pinia-plugin-persistedstate + 自定义 storage
这是更主流、可维护性更强的方式:
- 保留
pinia-plugin-persistedstate的配置能力(如paths、storage选 sessionStorage),只替换它的底层 storage 对象 - 自定义一个符合
StorageLike接口的对象,setItem里调用加密,getItem里调用解密 - 例如:
storage: { getItem: (key) => decrypt(localStorage.getItem(key)), setItem: (key, value) => localStorage.setItem(key, encrypt(value)) } - Vben Admin 等成熟项目就是这么做的,把 cipher 实例(AES-CTR 或 AES-CBC)注入到 storage 层,干净解耦
注意字段级控制与安全边界
加密不是全有或全无,得讲策略:
- 敏感字段(如 token)必须加密;非敏感字段(如 theme、sidebar 折叠状态)可明文存,减少性能开销
- 用
paths显式声明哪些字段参与持久化和加密,避免意外把 loading、error 等临时状态也加密存了 - 不要加密函数、Date、Map 等无法 JSON 序列化的值——插件会跳过或转成空对象,加密前先做数据清洗
- SSR 场景下 localStorage 不可用,插件会自动降级;但加密逻辑若没判断环境,可能报错,记得加
typeof window !== 'undefined'守卫










