You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何修复CryptoJS本地存储解密时的TypeError: Cannot read 'words'属性错误?

问题根源分析

刷新页面时触发TypeError: Cannot read properties of undefined (reading 'words'),本质是循环依赖导致解密时密钥未初始化:

  • 页面刷新后,redux通过localStorageSync的rehydrate功能尝试从本地存储恢复加密的auth状态,此时需要调用SECURE_DATA.decrypt方法。
  • 但你的key变量是在syncCondition中赋值的,而syncCondition要么在rehydrate完成后才执行,要么执行时的state是redux初始空状态(state.auth.token为undefined),导致解密时key为undefined,传入CryptoJS的AES解密方法后触发底层错误。
修复方案

方案一:分离密钥与加密数据,提前初始化密钥

将认证token(即解密密钥)存储在非加密的位置(比如cookie),在页面加载时直接读取初始化key,避免依赖redux state:

  1. 新增获取密钥的工具函数:
// 从cookie读取auth token(假设你的token存在名为authToken的cookie中)
const getAuthToken = (): string => {
  const cookieEntry = document.cookie.split('; ').find(row => row.startsWith('authToken='));
  return cookieEntry ? cookieEntry.split('=')[1] : '';
};
  1. 修改SECURE_DATA加入异常处理,避免崩溃:
const SECURE_DATA = {
  encrypt: (state: string) => {
    if (!key) throw new Error('Encryption key not initialized');
    return CryptoJs.AES.encrypt(state, key).toString();
  },
  decrypt: (state: string) => {
    if (!key) return '';
    try {
      return CryptoJs.AES.decrypt(state, key).toString(CryptoJs.enc.Utf8);
    } catch (err) {
      console.error('Decryption failed:', err);
      localStorage.removeItem('auth'); // 清除损坏的存储数据
      return '';
    }
  },
};
  1. 调整localStorageSyncReducer,提前初始化key:
export function localStorageSyncReducer(
  reducer: ActionReducer<StoreState>
): ActionReducer<StoreState> {
  // 页面加载时立即初始化密钥
  key = getAuthToken();

  return localStorageSync({
    keys: STORE_KEYS_TO_PERSIST,
    rehydrate: true,
    syncCondition(state) {
      // 同步更新密钥为最新的token
      key = state.auth.token || getAuthToken();
      return true;
    },
  })(reducer);
}

方案二:手动处理rehydrate,避免自动同步的循环依赖

禁用localStorageSync的自动rehydrate功能,手动读取并解密本地存储数据作为redux初始状态:

  1. 修改localStorageSyncReducer,关闭自动rehydrate:
export function localStorageSyncReducer(
  reducer: ActionReducer<StoreState>
): ActionReducer<StoreState> {
  return localStorageSync({
    keys: STORE_KEYS_TO_PERSIST,
    rehydrate: false, // 禁用自动恢复
    syncCondition(state) {
      key = state.auth.token;
      return true;
    },
  })(reducer);
}
  1. 在创建redux store时,手动加载并解密存储数据:
// 加载并解密持久化状态
const loadPersistedState = (): Partial<StoreState> | undefined => {
  const persistedAuth = localStorage.getItem('auth');
  if (!persistedAuth) return undefined;

  // 从cookie读取密钥(或其他可靠来源)
  const token = getAuthToken();
  if (!token) {
    localStorage.removeItem('auth');
    return undefined;
  }

  try {
    const decryptedAuthStr = CryptoJs.AES.decrypt(persistedAuth, token).toString(CryptoJs.enc.Utf8);
    return { auth: JSON.parse(decryptedAuthStr) };
  } catch (err) {
    console.error('Failed to load persisted state:', err);
    localStorage.removeItem('auth');
    return undefined;
  }
};

// 创建store时传入手动加载的初始状态
const store = createStore(
  reducers,
  loadPersistedState(),
  // 其他中间件/enhancers
);
额外注意事项
  • 不要依赖redux state作为解密密钥的唯一来源,因为rehydrate过程中state尚未初始化。
  • 加入异常处理可以避免单个存储项损坏导致整个页面崩溃。
  • 如果使用cookie存储token,确保cookie设置了合适的HttpOnly、Secure等安全属性。

内容的提问来源于stack exchange,提问作者vijay s

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 01:31:02