Skip to content

Commit f5448c1

Browse files
committed
feat: encrypt sensitive store data in localStorage
* 对存储在localStorage中的敏感数据进行加密
1 parent 0cc1cb5 commit f5448c1

File tree

7 files changed

+90
-1
lines changed

7 files changed

+90
-1
lines changed

apps/web-antd/.env

+3
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,6 @@ VITE_APP_TITLE=Vben Admin Antd
33

44
# 应用命名空间,用于缓存、store等功能的前缀,确保隔离
55
VITE_APP_NAMESPACE=vben-web-antd
6+
7+
# 缓存密钥,用于加解密储存于localStorage中的访问令牌、锁屏密码等数据
8+
VITE_STORAGE_CRYPTO_KEY=vben-storage-crypto-key

apps/web-ele/.env

+3
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,6 @@ VITE_APP_TITLE=Vben Admin Ele
33

44
# 应用命名空间,用于缓存、store等功能的前缀,确保隔离
55
VITE_APP_NAMESPACE=vben-web-ele
6+
7+
# 缓存密钥,用于加解密储存于localStorage中的访问令牌、锁屏密码等数据
8+
VITE_STORAGE_CRYPTO_KEY=vben-storage-crypto-key

apps/web-naive/.env

+3
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,6 @@ VITE_APP_TITLE=Vben Admin Naive
33

44
# 应用命名空间,用于缓存、store等功能的前缀,确保隔离
55
VITE_APP_NAMESPACE=vben-web-naive
6+
7+
# 缓存密钥,用于加解密储存于localStorage中的访问令牌、锁屏密码等数据
8+
VITE_STORAGE_CRYPTO_KEY=vben-storage-crypto-key
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
export function rc4(key: string, data: string): string {
2+
const S: number[] = [];
3+
let j: number = 0;
4+
const result: number[] = [];
5+
6+
// 初始化 S 盒
7+
for (let i = 0; i < 256; i++) {
8+
S[i] = i;
9+
}
10+
11+
// 初始置换
12+
for (let i = 0; i < 256; i++) {
13+
j =
14+
(j + (S[i] as number) + (key.codePointAt(i % key.length) as number)) %
15+
256;
16+
[S[i] as number, S[j] as number] = [S[j] as number, S[i] as number];
17+
}
18+
19+
let i: number = 0;
20+
j = 0;
21+
for (let k = 0; k < data.length; k++) {
22+
i = (i + 1) % 256;
23+
j = (j + (S[i] as number)) % 256;
24+
[S[i] as number, S[j] as number] = [S[j] as number, S[i] as number];
25+
const t = ((S[i] as number) + (S[j] as number)) % 256;
26+
const keystreamByte = S[t] as number;
27+
result.push((data.codePointAt(k) as number) ^ keystreamByte);
28+
}
29+
30+
return String.fromCharCode.apply(null, result);
31+
}
32+
33+
/**
34+
* 对数据进行序列化、加密操作
35+
* @param data 需要处理的数据
36+
* @param key 加密的密钥
37+
* @returns
38+
*/
39+
export function encrypt(data: any, key: string): string {
40+
const result = rc4(key, JSON.stringify(data));
41+
return btoa(result);
42+
}
43+
44+
/**
45+
* 对数据进行解密、反序列化操作
46+
* @param raw 需要处理的密文
47+
* @param key 解密的密钥
48+
* @returns
49+
*/
50+
export function decrypt(raw: string, key: string): any {
51+
try {
52+
const decodedData = atob(raw);
53+
const decryptedData = rc4(key, decodedData);
54+
return JSON.parse(decryptedData);
55+
} catch {
56+
return undefined;
57+
}
58+
}

packages/@core/base/shared/src/utils/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export * from './cn';
2+
export * from './crypto';
23
export * from './date';
34
export * from './diff';
45
export * from './dom';

packages/stores/src/setup.ts

+19-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import type { Pinia } from 'pinia';
22

33
import type { App } from 'vue';
44

5+
import { decrypt, encrypt } from '@vben-core/shared/utils';
6+
57
import { createPinia } from 'pinia';
68

79
let pinia: Pinia;
@@ -20,11 +22,27 @@ export async function initStores(app: App, options: InitStoreOptions) {
2022
const { createPersistedState } = await import('pinia-plugin-persistedstate');
2123
pinia = createPinia();
2224
const { namespace } = options;
25+
const STORAGE_CRYPTO_KEY =
26+
import.meta.env.VITE_STORAGE_CRYPTO_KEY || 'vben-admin-crypto';
27+
const IS_PROD = import.meta.env.PROD;
2328
pinia.use(
2429
createPersistedState({
2530
// key $appName-$store.id
2631
key: (storeKey) => `${namespace}-${storeKey}`,
27-
storage: localStorage,
32+
storage: IS_PROD
33+
? {
34+
getItem: (key) => {
35+
const value = localStorage.getItem(key);
36+
if (value) {
37+
return decrypt(value, STORAGE_CRYPTO_KEY);
38+
}
39+
return null;
40+
},
41+
setItem: (key, value) => {
42+
localStorage.setItem(key, encrypt(value, STORAGE_CRYPTO_KEY));
43+
},
44+
}
45+
: localStorage,
2846
}),
2947
);
3048
app.use(pinia);

playground/.env

+3
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,6 @@ VITE_APP_TITLE=Vben Admin
33

44
# 应用命名空间,用于缓存、store等功能的前缀,确保隔离
55
VITE_APP_NAMESPACE=vben-web-play
6+
7+
# 缓存密钥,用于加解密储存于localStorage中的访问令牌、锁屏密码等数据
8+
VITE_STORAGE_CRYPTO_KEY=vben-storage-crypto-key

0 commit comments

Comments
 (0)