fix(auth): use uuid device id outside production
This commit is contained in:
@@ -238,7 +238,8 @@ export const guestLoginActor = fromPromise<LoginStatus, void>(async () => {
|
||||
// ========== 启动 / 恢复时检查登录态 actor(只查不创) ==========
|
||||
// 由 <AuthStatusChecker /> 在 mount 时派发(一次性)。
|
||||
// 流程(纯只读):
|
||||
// 1. deviceIdentifier.getDeviceId() —— 无则通过 fingerprintjs 生成 + 落盘
|
||||
// 1. deviceIdentifier.getDeviceId() —— 无则生成 + 落盘
|
||||
// (生产环境使用 fingerprintjs;非生产环境使用本地 UUID)
|
||||
// (给后续 OAuth/email 登录准备 deviceId,不在 splash 首次访问就 auto-create 游客)
|
||||
// 2. AuthStorage.getLoginToken() —— 有 → "email"
|
||||
// 3. AuthStorage.getFacebookId() —— 有 → facebookIdLogin → "facebook"
|
||||
|
||||
@@ -3,14 +3,16 @@
|
||||
/**
|
||||
* 设备 ID 工具类
|
||||
*
|
||||
* 使用 fingerprintjs 生成客户端设备指纹。
|
||||
* 生产环境使用 fingerprintjs 生成客户端设备指纹。
|
||||
* 非生产环境使用本地 UUID,避免开发 / 预发布调试依赖真实设备指纹。
|
||||
* 原始 Dart: lib/data/services/device_id_service_impl.dart
|
||||
*
|
||||
* 注:仅在浏览器环境可用;SSR/Server Components 中调用会抛错。
|
||||
*/
|
||||
import { AuthStorage } from "../data/storage/auth/auth_storage";
|
||||
import { SpAsyncUtil } from "./storage";
|
||||
import { StorageKeys } from "../data/storage/storage_keys";
|
||||
import { AppEnvUtil } from "./app-env";
|
||||
import { SpAsyncUtil } from "./storage";
|
||||
|
||||
export class DeviceIdentifier {
|
||||
private cachedId: string | null = null;
|
||||
@@ -19,8 +21,8 @@ export class DeviceIdentifier {
|
||||
/**
|
||||
* 获取或生成设备 ID
|
||||
*
|
||||
* 优先级:内存缓存 → localStorage 缓存 → fingerprintjs 生成
|
||||
* 并发安全:多个并发调用会共享同一个 fingerprintjs 加载 Promise
|
||||
* 优先级:内存缓存 → localStorage 缓存 → 非生产 UUID / 生产 fingerprintjs
|
||||
* 并发安全:多个并发调用会共享同一个初始化 Promise
|
||||
*/
|
||||
async getDeviceId(): Promise<string> {
|
||||
if (this.cachedId) return this.cachedId;
|
||||
@@ -55,17 +57,51 @@ export class DeviceIdentifier {
|
||||
);
|
||||
}
|
||||
|
||||
// 3. 动态加载 fingerprintjs(避免 Next.js SSR 打包分析时执行)
|
||||
// 3. 非生产环境不使用真实设备指纹,生成并持久化一个本地 UUID 即可。
|
||||
if (!AppEnvUtil.isProduction()) {
|
||||
const deviceId = createUuidV4();
|
||||
await AuthStorage.getInstance().setDeviceId(deviceId);
|
||||
return deviceId;
|
||||
}
|
||||
|
||||
// 4. 生产环境动态加载 fingerprintjs(避免 Next.js SSR 打包分析时执行)
|
||||
const { load } = await import("@fingerprintjs/fingerprintjs");
|
||||
const fp = await load();
|
||||
const result = await fp.get();
|
||||
|
||||
// 4. 持久化
|
||||
// 5. 持久化
|
||||
await AuthStorage.getInstance().setDeviceId(result.visitorId);
|
||||
return result.visitorId;
|
||||
}
|
||||
}
|
||||
|
||||
function createUuidV4(): string {
|
||||
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
||||
return crypto.randomUUID();
|
||||
}
|
||||
|
||||
const bytes = new Uint8Array(16);
|
||||
if (typeof crypto !== "undefined" && crypto.getRandomValues) {
|
||||
crypto.getRandomValues(bytes);
|
||||
} else {
|
||||
for (let i = 0; i < bytes.length; i += 1) {
|
||||
bytes[i] = Math.floor(Math.random() * 256);
|
||||
}
|
||||
}
|
||||
|
||||
bytes[6] = (bytes[6] & 0x0f) | 0x40;
|
||||
bytes[8] = (bytes[8] & 0x3f) | 0x80;
|
||||
|
||||
const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0"));
|
||||
return [
|
||||
hex.slice(0, 4).join(""),
|
||||
hex.slice(4, 6).join(""),
|
||||
hex.slice(6, 8).join(""),
|
||||
hex.slice(8, 10).join(""),
|
||||
hex.slice(10, 16).join(""),
|
||||
].join("-");
|
||||
}
|
||||
|
||||
/**
|
||||
* 全局单例
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user