import globalConfig from '@/config'; import { stringMd5, aesCbcEncryptString, aesCbcDecryptString, rabbitEncryptString, rabbitDecryptString, } from '@/utils/crypto'; const storageKeyIV = stringMd5(`${globalConfig.app.title}_${globalConfig.app.version}`).slice(0, 8); export function encryptKey(key: string) { return rabbitEncryptString(key, globalConfig.security.storageEncryptionKey, storageKeyIV); } export function decryptKey(key: string) { return rabbitDecryptString(key, globalConfig.security.storageEncryptionKey, storageKeyIV); } export function encryptData(data: string) { return aesCbcEncryptString(data, globalConfig.security.storageEncryptionKey); } export function decryptData(data: string) { return aesCbcDecryptString(data, globalConfig.security.storageEncryptionKey); } export interface StorageOptions { /** * 过期时间(秒), 0 为永远不过期 * @default 0 */ expire?: number; /** * 是否加密key * @default false */ encryptKey?: boolean; /** * 是否加密数据 * @default false */ encryptData?: boolean; } interface StorageData { data: T; expire: number; } function shouldEncryptKey(functionOptions?: StorageOptions, instanceOptions?: StorageOptions) { if (globalConfig.security.enableStorageEncryption) { return functionOptions?.encryptKey ?? instanceOptions?.encryptKey ?? false; } return false; } function shouldEncryptData(funcOpts?: StorageOptions, instanceOpts?: StorageOptions) { if (globalConfig.security.enableStorageEncryption) { return funcOpts?.encryptData ?? instanceOpts?.encryptData ?? false; } return false; } /** * 创建 localStorage 工具实例 * @param opts {StorageOptions} 本实例使用的全局配置 * @returns */ export function createLocalStorage({ expire: confExpire = 0, encryptKey: confEncryptKey = false, encryptData: confEncryptData = false, }: StorageOptions = {}) { /** * 保存数据 * @param key * @param data * @param opts 当传递了 opts 时,opts 中存在的配置项会覆盖当前实例的全局配置中的配置项。 */ function set(key: string, data: T, opts?: StorageOptions) { const expire = opts?.expire ?? confExpire; const isEncryptKey = shouldEncryptKey(opts, { encryptKey: confEncryptKey }); const isEncryptData = shouldEncryptData(opts, { encryptData: confEncryptData }); const storageData: StorageData = { data, expire: expire !== 0 ? new Date().getTime() + expire * 1000 : 0, }; const json = JSON.stringify(storageData); const finalKey = isEncryptKey ? encryptKey(key) : key; const finalData = isEncryptData ? encryptData(json) : json; window.localStorage.setItem(finalKey, finalData); } /** * 读取数据 * @param key * @param opts 当传递了 opts 时,opts 中存在的配置项会覆盖当前实例的全局配置中的配置项。 * @returns */ function get(key: string, opts?: StorageOptions) { const isEncryptKey = shouldEncryptKey(opts, { encryptKey: confEncryptKey }); const isEncryptData = shouldEncryptData(opts, { encryptData: confEncryptData }); const finalKey = isEncryptKey ? encryptKey(key) : key; let data = window.localStorage.getItem(finalKey); if (!data) return null; if (isEncryptData) { data = decryptData(data); } if (!data) return null; let storageData: StorageData | null = null; try { storageData = JSON.parse(data); } catch { // Prevent failure } if (storageData) { const { data, expire = 0 } = storageData; if (expire === 0 || expire >= Date.now()) return data as T; } return null; } /** * 删除数据 * @param key * @param opts 当传递了 opts 时,opts 中存在的配置项会覆盖当前实例的全局配置中的配置项。 */ function remove(key: string, opts?: StorageOptions) { const isEncryptKey = shouldEncryptKey(opts, { encryptKey: confEncryptKey }); const finalKey = isEncryptKey ? encryptKey(key) : key; window.localStorage.removeItem(finalKey); } /** * 清除 localStorage 中的所有数据。**慎用** */ function clear() { window.localStorage.clear(); } return { set, get, remove, clear }; } /** * 创建 sessionStorage 工具实例 * @param opts {StorageOptions} 本实例使用的全局配置 * @returns */ export function createSessionStorage({ expire: confExpire = 0, encryptKey: confEncryptKey = false, encryptData: confEncryptData = false, }: StorageOptions = {}) { /** * 保存数据 * @param key * @param data * @param opts 当传递了 opts 时,opts 中存在的配置项会覆盖当前实例的全局配置中的配置项。 */ function set(key: string, data: T, opts?: StorageOptions) { const expire = opts?.expire ?? confExpire; const isEncryptKey = shouldEncryptKey(opts, { encryptKey: confEncryptKey }); const isEncryptData = shouldEncryptData(opts, { encryptData: confEncryptData }); const storageData: StorageData = { data, expire: expire !== 0 ? new Date().getTime() + expire * 1000 : 0, }; const json = JSON.stringify(storageData); const finalKey = isEncryptKey ? encryptKey(key) : key; const finalData = isEncryptData ? encryptData(json) : json; window.sessionStorage.setItem(finalKey, finalData); } /** * 读取数据 * @param key * @param opts 当传递了 opts 时,opts 中存在的配置项会覆盖当前实例的全局配置中的配置项。 * @returns */ function get(key: string, opts?: StorageOptions) { const isEncryptKey = shouldEncryptKey(opts, { encryptKey: confEncryptKey }); const isEncryptData = shouldEncryptData(opts, { encryptData: confEncryptData }); const finalKey = isEncryptKey ? encryptKey(key) : key; let data = window.sessionStorage.getItem(finalKey); if (!data) return null; if (isEncryptData) { data = decryptData(data); } if (!data) return null; let storageData: StorageData | null = null; try { storageData = JSON.parse(data); } catch { // Prevent failure } if (storageData) { const { data, expire = 0 } = storageData; if (expire === 0 || expire >= Date.now()) return data as T; } return null; } /** * 删除数据 * @param key * @param opts 当传递了 opts 时,opts 中存在的配置项会覆盖当前实例的全局配置中的配置项。 */ function remove(key: string, opts?: StorageOptions) { const isEncryptKey = shouldEncryptKey(opts, { encryptKey: confEncryptKey }); const finalKey = isEncryptKey ? encryptKey(key) : key; window.sessionStorage.removeItem(finalKey); } /** * 清除 sessionStorage 中的所有数据。**慎用** */ function clear() { window.sessionStorage.clear(); } return { set, get, remove, clear }; } export const ls = createLocalStorage({ expire: 60 * 60 * 24 * 7, encryptKey: true, encryptData: true, }); export const ss = createSessionStorage({ expire: 60 * 60 * 24 * 7, encryptKey: true, encryptData: true, });