import EncryptUtil from './EncryptUtil'; /** * ============================================================ * 登录后数据存储工具 * 与 Vue2 基座 login.js 保持一致的 token / session 处理逻辑 * 只保留 V4 相关逻辑 * ============================================================ */ /** * RSA 私钥(与 Vue2 基座 EncryptUtil.js 中 RSADecrypt 使用的私钥一致) * 用于解密服务端登录响应中的 session 字段,获取 AES 会话密钥 */ const RSA_PRIVATE_KEY = 'MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAIRrmftLDHCQqREEJ132Onu+W3vmFbdF7QD751SrcDDGDTfzuz1zBuElvkHhuDBb7KZkXrCIe+MhvX2IvxcLObl3faX+evYlnfj2HRbF0hIpQLuIq22tL06ZcV5w7wqLxUZRpFElIFm8gZTkUvfKXVuHw89e4daDVhU5hK3GHNGTAgMBAAECgYABiINrFaE1E8pkBYx1JJA5yuhL73aUktfd2TeCU00vFg6kyrWCI85Sa2RKu/6CJNZWeOFgdubEUv7a22tRrNIZb3yUMaqtTwSso78mspIOJqjWXTkTH9WPElfTcdpdIse/lgZtPz6egxkuhadSvwrM9Y6NgusiW/5+x95Ct08iOQJBAN5aK+7uISURvGQj2EaRtgGEd8+d4oHl+BYvvTeG3qSgUikHQW3j0sp4gXPw2kxw6sjVgLFOc4FB6LGqwzOTzokCQQCYdYG8ty3Uo/ebUlNzeJFxHXjy/KvBSytAUzAXkRu3nZrkEaPQsi3dgOkZgk+F1fMDzfQ4EbDIU6xvqOoZXHg7AkATCW9XfoXR8anKfRMoP5Nwn9HOMbtR2cmaxK2TknV/bMZ8AsYETYwfj5+tuIJIJybC2RyykX/sIiN1CqS5xr7ZAkArj19rMRdaKyMi8MnBM1Cy9g3Jt2HHj5ejAGG8SgyWUOShh1y70z0BjcSMMkxQXAncK2s83ekZw7aADM4eQupjAkARRgTwwMOnn3IoKmQusKhZk0uxilZ4Zc2LH6Z4GiWnvteM0W8Zw4Z1lJUcjgQq3dGqL2RdmzeQZ+HgPIOXrZVK' /** localStorage 中各字段的 key 名,与 Vue2 基座保持一致 */ export const STORAGE_KEYS = { /** V4 Bearer token */ TOKEN: 'token', /** V4 AES 会话密钥(Hex 格式,用于接口加密) */ V4_SESSION_KEY: 'v4-session-key', /** 当前用户信息(含 functions 等) */ CURRENT_USER: 'currentUser', /** 菜单/功能列表 */ FUNCTIONS: 'functions', /** 字典数据 */ DICTIONARY: 'dictionary', }; /** * 解析 V4 登录响应,统一处理 data 嵌套结构 * 后端有时返回 { access_token } 有时返回 { data: { access_token } } */ function unwrapV4LoginPayload(res: any): any | null { if (!res || typeof res !== 'object') return null; if (res.access_token != null) return res; if (res.data && typeof res.data === 'object' && res.data.access_token != null) { return res.data; } return null; } /** * 解密登录响应中的 session 字段,写入 v4-session-key * 只有后端开启 requestEncrypt 且返回 session 时才生效 * 对应 Vue2 基座 login.js 中的 setV4SessionKey * @param res V4 登录响应体(unwrap 后的 payload) */ export function setV4SessionKey(res: any): void { if (!res?.session) return; // 读取 webConfig,判断服务端是否开启了请求加密 // try { // const webConfigStr = localStorage.getItem( // process.env.VUE_APP_WEB_CONFIG_KEY || 'webConfig', // ); // const webConfig = webConfigStr ? JSON.parse(webConfigStr) : {}; // if (!webConfig?.setting?.requestEncrypt) return; // } catch { // return; // } // RSA 解密 session,得到 Hex 格式的 AES 会话密钥 const sessionKey = EncryptUtil.RSADecryptRaw(res.session, RSA_PRIVATE_KEY); if (sessionKey) { localStorage.setItem(STORAGE_KEYS.V4_SESSION_KEY, sessionKey); } } /** * 保存 V4 登录 token,同时触发 session 密钥存储 * 对应 Vue2 基座 login.js 中的 setV4AccessToken * @param res V4 登录接口原始响应 */ export function setV4AccessToken(res: any): void { const payload = unwrapV4LoginPayload(res); if (!payload?.access_token) return; localStorage.setItem(STORAGE_KEYS.TOKEN, payload.access_token); // expire 单位:分钟;存储过期时间供其他地方判断 const expireMs = (payload.expire ?? 0) * 60 * 1000; const expireAt = new Date(Date.now() + expireMs); localStorage.setItem('token_expire_at', expireAt.toISOString()); // 解密并存储 AES 会话密钥 setV4SessionKey(payload); } /** * 保存用户信息、菜单、字典到 localStorage * 对应 Vue2 基座 afterGeneral 中的数据持久化部分 * @param userData getRoutesConfig 返回的用户+权限数据 * @param functions 经过排序和预加载的菜单列表 * @param dictionary 字典数据 */ export function saveUserData( userData: any, functions: any[], dictionary: any, ): void { localStorage.setItem(STORAGE_KEYS.CURRENT_USER, JSON.stringify(userData)); localStorage.setItem(STORAGE_KEYS.FUNCTIONS, JSON.stringify(functions)); localStorage.setItem(STORAGE_KEYS.DICTIONARY, JSON.stringify(dictionary)); } /** * 清除所有登录相关的本地存储数据(登出时调用) */ export function clearLoginData(): void { Object.values(STORAGE_KEYS).forEach((key) => localStorage.removeItem(key)); localStorage.removeItem('token_expire_at'); }