diff --git a/apps/miniprogram/src/services/request.ts b/apps/miniprogram/src/services/request.ts index a5a5cfe..b835eb1 100644 --- a/apps/miniprogram/src/services/request.ts +++ b/apps/miniprogram/src/services/request.ts @@ -1,4 +1,5 @@ import Taro from '@tarojs/taro'; +import { secureGet, secureSet, secureRemove } from '@/utils/secure-storage'; const BASE_URL = process.env.TARO_APP_API_URL || 'http://localhost:3000/api/v1'; @@ -10,7 +11,7 @@ interface ApiResponse { async function getHeaders(): Promise> { const headers: Record = { 'Content-Type': 'application/json' }; - const token = Taro.getStorageSync('access_token'); + const token = secureGet('access_token'); if (token) headers['Authorization'] = `Bearer ${token}`; const patientId = Taro.getStorageSync('current_patient_id'); if (patientId) headers['X-Patient-Id'] = patientId; @@ -20,7 +21,7 @@ async function getHeaders(): Promise> { } async function tryRefreshToken(): Promise { - const refreshToken = Taro.getStorageSync('refresh_token'); + const refreshToken = secureGet('refresh_token'); if (!refreshToken) return false; try { const res = await Taro.request({ @@ -29,15 +30,15 @@ async function tryRefreshToken(): Promise { data: { refresh_token: refreshToken }, }); if (res.statusCode === 200 && res.data?.success) { - Taro.setStorageSync('access_token', res.data.data.access_token); - Taro.setStorageSync('refresh_token', res.data.data.refresh_token); + secureSet('access_token', res.data.data.access_token); + secureSet('refresh_token', res.data.data.refresh_token); return true; } } catch (err) { console.error('[tryRefreshToken] token 刷新失败:', err); } - Taro.removeStorageSync('access_token'); - Taro.removeStorageSync('refresh_token'); + secureRemove('access_token'); + secureRemove('refresh_token'); return false; } diff --git a/apps/miniprogram/src/stores/auth.ts b/apps/miniprogram/src/stores/auth.ts index 3c087e8..fb31929 100644 --- a/apps/miniprogram/src/stores/auth.ts +++ b/apps/miniprogram/src/stores/auth.ts @@ -1,6 +1,7 @@ import { create } from 'zustand'; import Taro from '@tarojs/taro'; import * as authApi from '@/services/auth'; +import { secureGet, secureSet, secureRemove } from '@/utils/secure-storage'; interface BindPhoneResp { access_token: string; @@ -33,8 +34,8 @@ export const useAuthStore = create((set, get) => ({ loading: false, restore: () => { - const token = Taro.getStorageSync('access_token') || null; - const refreshToken = Taro.getStorageSync('refresh_token') || null; + const token = secureGet('access_token') || null; + const refreshToken = secureGet('refresh_token') || null; const user = Taro.getStorageSync('user') || null; const currentPatient = Taro.getStorageSync('current_patient') || null; set({ token, refreshToken, user, currentPatient }); @@ -46,8 +47,8 @@ export const useAuthStore = create((set, get) => ({ const resp = await authApi.wechatLogin(code); if (resp.bound && resp.token) { const { access_token, refresh_token, user } = resp.token; - Taro.setStorageSync('access_token', access_token); - Taro.setStorageSync('refresh_token', refresh_token); + secureSet('access_token', access_token); + secureSet('refresh_token', refresh_token); Taro.setStorageSync('user', user); Taro.setStorageSync('tenant_id', user.tenant_id || ''); set({ token: access_token, refreshToken: refresh_token, user, loading: false }); @@ -73,8 +74,8 @@ export const useAuthStore = create((set, get) => ({ } const resp = await authApi.wechatBindPhone(openid, encryptedData, iv) as BindPhoneResp; const { access_token, refresh_token, user } = resp; - Taro.setStorageSync('access_token', access_token); - Taro.setStorageSync('refresh_token', refresh_token); + secureSet('access_token', access_token); + secureSet('refresh_token', refresh_token); Taro.setStorageSync('user', user); Taro.setStorageSync('tenant_id', user.tenant_id || ''); Taro.removeStorageSync('wechat_openid'); @@ -105,8 +106,8 @@ export const useAuthStore = create((set, get) => ({ }, logout: () => { - Taro.removeStorageSync('access_token'); - Taro.removeStorageSync('refresh_token'); + secureRemove('access_token'); + secureRemove('refresh_token'); Taro.removeStorageSync('user'); Taro.removeStorageSync('current_patient'); Taro.removeStorageSync('current_patient_id'); diff --git a/apps/miniprogram/src/utils/secure-storage.ts b/apps/miniprogram/src/utils/secure-storage.ts new file mode 100644 index 0000000..77f377b --- /dev/null +++ b/apps/miniprogram/src/utils/secure-storage.ts @@ -0,0 +1,42 @@ +import Taro from '@tarojs/taro'; + +const XOR_KEY = 'hms_mp_2026_secure_key'; + +function xorTransform(value: string): string { + let result = ''; + for (let i = 0; i < value.length; i++) { + result += String.fromCharCode(value.charCodeAt(i) ^ XOR_KEY.charCodeAt(i % XOR_KEY.length)); + } + return result; +} + +function toBase64(str: string): string { + return btoa(unescape(encodeURIComponent(str))); +} + +function fromBase64(b64: string): string { + try { + return decodeURIComponent(escape(atob(b64))); + } catch { + return ''; + } +} + +export function secureSet(key: string, value: string): void { + const obfuscated = toBase64(xorTransform(value)); + Taro.setStorageSync(key, obfuscated); +} + +export function secureGet(key: string): string { + const raw = Taro.getStorageSync(key); + if (!raw || typeof raw !== 'string') return ''; + try { + return xorTransform(fromBase64(raw)); + } catch { + return ''; + } +} + +export function secureRemove(key: string): void { + Taro.removeStorageSync(key); +}