fix(mp): 存储层语义统一 + UTF-16 截断修复

- secureGet: 增加 TextEncoder/TextDecoder 替代 charCodeAt 避免 UTF-16 截断
- secureGet: _es_ 前缀键返回空时增加明文键 fallback(对齐 storageGet 语义)
- request.ts safeGet / auth.ts storageGet: 简化为直接委托 secureGet
This commit is contained in:
iven
2026-05-21 22:34:14 +08:00
parent ee7dd0d6e1
commit fd994edf3e
3 changed files with 13 additions and 21 deletions

View File

@@ -21,11 +21,7 @@ const ERROR_CODE_MAP: Record<string, string> = {
}; };
function safeGet(key: string): string { function safeGet(key: string): string {
try { return secureGet(key);
return secureGet(key);
} catch {
return Taro.getStorageSync(key) || '';
}
} }
// --- Concurrency limiter --- // --- Concurrency limiter ---

View File

@@ -4,11 +4,9 @@ import * as authApi from '@/services/auth';
import { secureGet, secureSet, secureRemove } from '@/utils/secure-storage'; import { secureGet, secureSet, secureRemove } from '@/utils/secure-storage';
import { clearRequestCache, markLoggingOut, clearLoggingOut, setCachedPatientId } from '@/services/request'; import { clearRequestCache, markLoggingOut, clearLoggingOut, setCachedPatientId } from '@/services/request';
// secureGet fallback: _es_ 加密键为空时尝试明文键(兼容 MCP 注入等场景) // secureGet 已内置明文键 fallback,无需再手动 fallback
function storageGet(key: string): string { function storageGet(key: string): string {
const val = secureGet(key); return secureGet(key);
if (val) return val;
return Taro.getStorageSync(key) || '';
} }
import { resetAllStores } from './index'; import { resetAllStores } from './index';

View File

@@ -12,11 +12,9 @@ function xorEncrypt(data: string, key: string): string {
function toBase64(str: string): string { function toBase64(str: string): string {
try { try {
const buffer = new Uint8Array(str.length); const encoder = new TextEncoder();
for (let i = 0; i < str.length; i++) { const uint8 = encoder.encode(str);
buffer[i] = str.charCodeAt(i); return Taro.arrayBufferToBase64(uint8.buffer as ArrayBuffer);
}
return Taro.arrayBufferToBase64(buffer.buffer);
} catch { } catch {
return ''; return '';
} }
@@ -25,12 +23,8 @@ function toBase64(str: string): string {
function fromBase64(b64: string): string { function fromBase64(b64: string): string {
try { try {
const buffer = Taro.base64ToArrayBuffer(b64); const buffer = Taro.base64ToArrayBuffer(b64);
const arr = new Uint8Array(buffer); const decoder = new TextDecoder();
let result = ''; return decoder.decode(new Uint8Array(buffer));
for (let i = 0; i < arr.length; i++) {
result += String.fromCharCode(arr[i]);
}
return result;
} catch { } catch {
return ''; return '';
} }
@@ -55,7 +49,11 @@ export function secureSet(key: string, value: string): void {
export function secureGet(key: string): string { export function secureGet(key: string): string {
const prefixedKey = STORAGE_PREFIX + key; const prefixedKey = STORAGE_PREFIX + key;
const raw = Taro.getStorageSync(prefixedKey); const raw = Taro.getStorageSync(prefixedKey);
if (!raw || typeof raw !== 'string') return ''; if (!raw || typeof raw !== 'string') {
// fallback: 尝试读取明文键(兼容 MCP 注入等场景)
const plain = Taro.getStorageSync(key);
return (plain && typeof plain === 'string') ? plain : '';
}
// 始终尝试 base64 解码 + XOR 解密secureSet 的写入格式) // 始终尝试 base64 解码 + XOR 解密secureSet 的写入格式)
try { try {