2. iOS: 组合设备型号 + 系统版本 + identifierForVendor,持久化存储到钥匙串 3. macOS: 使用硬件UUID + 序列号 4. Windows: 使用主板UUID + CPU信息 5. Linux: 使用machine-id + 硬件信息 (cherry picked from commit 1be3037f715548a1efa4cc5d7d204b989878557a)
443 lines
14 KiB
Dart
443 lines
14 KiB
Dart
import 'dart:io';
|
|
import 'package:device_info_plus/device_info_plus.dart';
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter_udid/flutter_udid.dart';
|
|
import '../utils/kr_secure_storage.dart';
|
|
import '../utils/kr_log_util.dart';
|
|
import 'package:crypto/crypto.dart';
|
|
import 'dart:convert';
|
|
|
|
/// 设备信息服务
|
|
/// 用于获取设备唯一标识和其他设备信息
|
|
class KRDeviceInfoService {
|
|
static final KRDeviceInfoService _instance = KRDeviceInfoService._internal();
|
|
factory KRDeviceInfoService() => _instance;
|
|
KRDeviceInfoService._internal();
|
|
|
|
final DeviceInfoPlugin _deviceInfo = DeviceInfoPlugin();
|
|
String? _deviceId;
|
|
Map<String, dynamic>? _deviceDetails;
|
|
|
|
// 获取设备唯一标识
|
|
String? get deviceId => _deviceId;
|
|
|
|
// 获取设备详细信息
|
|
Map<String, dynamic>? get deviceDetails => _deviceDetails;
|
|
|
|
/// 初始化设备信息
|
|
Future<void> initialize() async {
|
|
try {
|
|
print('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
print('📱 开始初始化设备信息服务');
|
|
print('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
KRLogUtil.kr_i('📱 开始初始化设备信息', tag: 'KRDeviceInfoService');
|
|
|
|
_deviceId = await _getDeviceId();
|
|
_deviceDetails = await _getDeviceDetails();
|
|
|
|
print('✅ 设备信息初始化成功');
|
|
print('📱 设备ID: $_deviceId');
|
|
print('📱 设备平台: ${getPlatformName()}');
|
|
print('📱 设备型号: ${getDeviceModel()}');
|
|
print('📱 系统版本: ${getOSVersion()}');
|
|
print('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
|
|
KRLogUtil.kr_i('✅ 设备信息初始化成功', tag: 'KRDeviceInfoService');
|
|
KRLogUtil.kr_i('📱 设备ID - $_deviceId', tag: 'KRDeviceInfoService');
|
|
KRLogUtil.kr_i('📱 设备详情 - $_deviceDetails', tag: 'KRDeviceInfoService');
|
|
} catch (e) {
|
|
print('❌ 设备信息初始化失败: $e');
|
|
KRLogUtil.kr_e('❌ 设备信息初始化失败 - $e', tag: 'KRDeviceInfoService');
|
|
}
|
|
}
|
|
|
|
/// 获取设备唯一标识
|
|
/// 使用多因子组合策略确保唯一性和稳定性
|
|
Future<String> _getDeviceId() async {
|
|
try {
|
|
String identifier;
|
|
|
|
if (Platform.isAndroid) {
|
|
identifier = await _getAndroidDeviceId();
|
|
} else if (Platform.isIOS) {
|
|
identifier = await _getIOSDeviceId();
|
|
} else if (Platform.isMacOS) {
|
|
identifier = await _getMacOSDeviceId();
|
|
} else if (Platform.isWindows) {
|
|
identifier = await _getWindowsDeviceId();
|
|
} else if (Platform.isLinux) {
|
|
identifier = await _getLinuxDeviceId();
|
|
} else {
|
|
// Web或其他平台,使用生成的UUID
|
|
identifier = await _getOrCreateStoredDeviceId();
|
|
}
|
|
|
|
// 如果获取失败,使用存储的或生成新的ID
|
|
if (identifier.isEmpty) {
|
|
identifier = await _getOrCreateStoredDeviceId();
|
|
}
|
|
|
|
return identifier;
|
|
} catch (e) {
|
|
print('❌ 获取设备ID失败: $e');
|
|
KRLogUtil.kr_e('❌ 获取设备ID失败 - $e', tag: 'KRDeviceInfoService');
|
|
// 如果获取失败,返回存储的或生成新的ID
|
|
return await _getOrCreateStoredDeviceId();
|
|
}
|
|
}
|
|
|
|
/// Android设备ID - 多因子组合
|
|
/// 组合: AndroidID + 设备型号 + 主板信息 + 硬件信息
|
|
Future<String> _getAndroidDeviceId() async {
|
|
try {
|
|
final androidInfo = await _deviceInfo.androidInfo;
|
|
|
|
// 优先使用 flutter_udid (封装了多种Android标识获取方式)
|
|
String udid = await FlutterUdid.consistentUdid;
|
|
|
|
// 构建多因子字符串
|
|
final factors = [
|
|
udid,
|
|
androidInfo.id, // Android ID
|
|
androidInfo.board, // 主板
|
|
androidInfo.bootloader, // Bootloader
|
|
androidInfo.brand, // 品牌
|
|
androidInfo.device, // 设备名
|
|
androidInfo.fingerprint, // 系统指纹
|
|
androidInfo.hardware, // 硬件名
|
|
androidInfo.manufacturer, // 制造商
|
|
androidInfo.model, // 型号
|
|
androidInfo.product, // 产品名
|
|
];
|
|
|
|
// 过滤空值并组合
|
|
final combined = factors
|
|
.where((f) => f != null && f.isNotEmpty)
|
|
.join('|');
|
|
|
|
// 生成SHA256哈希
|
|
final bytes = utf8.encode(combined);
|
|
final hash = sha256.convert(bytes);
|
|
|
|
print('📱 Android多因子ID生成 - 因子数: ${factors.where((f) => f != null && f.isNotEmpty).length}');
|
|
KRLogUtil.kr_i('📱 Android多因子ID - $hash', tag: 'KRDeviceInfoService');
|
|
|
|
return hash.toString();
|
|
} catch (e) {
|
|
print('❌ Android设备ID获取失败: $e');
|
|
KRLogUtil.kr_e('❌ Android设备ID获取失败 - $e', tag: 'KRDeviceInfoService');
|
|
return '';
|
|
}
|
|
}
|
|
|
|
/// iOS设备ID - 优先使用identifierForVendor,配合持久化存储
|
|
/// iOS限制较严,无法获取IMEI等敏感信息
|
|
Future<String> _getIOSDeviceId() async {
|
|
try {
|
|
final iosInfo = await _deviceInfo.iosInfo;
|
|
|
|
// 优先使用 flutter_udid
|
|
String udid = await FlutterUdid.consistentUdid;
|
|
|
|
// 构建多因子字符串
|
|
final factors = [
|
|
udid,
|
|
iosInfo.identifierForVendor ?? '', // Vendor标识
|
|
iosInfo.model, // 型号
|
|
iosInfo.systemName, // 系统名
|
|
iosInfo.systemVersion, // 系统版本
|
|
iosInfo.name, // 设备名(如"iPhone 14 Pro")
|
|
iosInfo.utsname.machine, // 机器类型
|
|
];
|
|
|
|
final combined = factors
|
|
.where((f) => f.isNotEmpty)
|
|
.join('|');
|
|
|
|
final bytes = utf8.encode(combined);
|
|
final hash = sha256.convert(bytes);
|
|
|
|
print('📱 iOS多因子ID生成 - 因子数: ${factors.where((f) => f.isNotEmpty).length}');
|
|
KRLogUtil.kr_i('📱 iOS多因子ID - $hash', tag: 'KRDeviceInfoService');
|
|
|
|
return hash.toString();
|
|
} catch (e) {
|
|
print('❌ iOS设备ID获取失败: $e');
|
|
KRLogUtil.kr_e('❌ iOS设备ID获取失败 - $e', tag: 'KRDeviceInfoService');
|
|
return '';
|
|
}
|
|
}
|
|
|
|
/// macOS设备ID - 使用硬件UUID
|
|
Future<String> _getMacOSDeviceId() async {
|
|
try {
|
|
final macInfo = await _deviceInfo.macOsInfo;
|
|
|
|
// 优先使用 flutter_udid
|
|
String udid = await FlutterUdid.consistentUdid;
|
|
|
|
// 构建多因子字符串
|
|
final factors = [
|
|
udid,
|
|
macInfo.systemGUID ?? '', // 系统GUID (最稳定)
|
|
macInfo.model, // 型号
|
|
macInfo.hostName, // 主机名
|
|
macInfo.arch, // 架构
|
|
macInfo.kernelVersion, // 内核版本
|
|
];
|
|
|
|
final combined = factors
|
|
.where((f) => f.isNotEmpty)
|
|
.join('|');
|
|
|
|
final bytes = utf8.encode(combined);
|
|
final hash = sha256.convert(bytes);
|
|
|
|
print('📱 macOS多因子ID生成 - 因子数: ${factors.where((f) => f.isNotEmpty).length}');
|
|
KRLogUtil.kr_i('📱 macOS多因子ID - $hash', tag: 'KRDeviceInfoService');
|
|
|
|
return hash.toString();
|
|
} catch (e) {
|
|
print('❌ macOS设备ID获取失败: $e');
|
|
KRLogUtil.kr_e('❌ macOS设备ID获取失败 - $e', tag: 'KRDeviceInfoService');
|
|
return '';
|
|
}
|
|
}
|
|
|
|
/// Windows设备ID - 使用机器GUID
|
|
Future<String> _getWindowsDeviceId() async {
|
|
try {
|
|
final windowsInfo = await _deviceInfo.windowsInfo;
|
|
|
|
// 优先使用 flutter_udid
|
|
String udid = await FlutterUdid.consistentUdid;
|
|
|
|
// 构建多因子字符串
|
|
final factors = [
|
|
udid,
|
|
windowsInfo.deviceId, // 设备ID
|
|
windowsInfo.computerName, // 计算机名
|
|
windowsInfo.productName, // 产品名
|
|
windowsInfo.numberOfCores.toString(), // CPU核心数
|
|
windowsInfo.systemMemoryInMegabytes.toString(), // 内存大小
|
|
];
|
|
|
|
final combined = factors
|
|
.where((f) => f.isNotEmpty)
|
|
.join('|');
|
|
|
|
final bytes = utf8.encode(combined);
|
|
final hash = sha256.convert(bytes);
|
|
|
|
print('📱 Windows多因子ID生成 - 因子数: ${factors.where((f) => f.isNotEmpty).length}');
|
|
KRLogUtil.kr_i('📱 Windows多因子ID - $hash', tag: 'KRDeviceInfoService');
|
|
|
|
return hash.toString();
|
|
} catch (e) {
|
|
print('❌ Windows设备ID获取失败: $e');
|
|
KRLogUtil.kr_e('❌ Windows设备ID获取失败 - $e', tag: 'KRDeviceInfoService');
|
|
return '';
|
|
}
|
|
}
|
|
|
|
/// Linux设备ID - 使用machine-id
|
|
Future<String> _getLinuxDeviceId() async {
|
|
try {
|
|
final linuxInfo = await _deviceInfo.linuxInfo;
|
|
|
|
// 优先使用 flutter_udid
|
|
String udid = await FlutterUdid.consistentUdid;
|
|
|
|
// 构建多因子字符串
|
|
final factors = [
|
|
udid,
|
|
linuxInfo.machineId ?? '', // Machine ID (最稳定)
|
|
linuxInfo.id, // 发行版ID
|
|
linuxInfo.name, // 发行版名称
|
|
linuxInfo.version ?? '', // 版本
|
|
linuxInfo.variant ?? '', // 变体
|
|
];
|
|
|
|
final combined = factors
|
|
.where((f) => f.isNotEmpty)
|
|
.join('|');
|
|
|
|
final bytes = utf8.encode(combined);
|
|
final hash = sha256.convert(bytes);
|
|
|
|
print('📱 Linux多因子ID生成 - 因子数: ${factors.where((f) => f.isNotEmpty).length}');
|
|
KRLogUtil.kr_i('📱 Linux多因子ID - $hash', tag: 'KRDeviceInfoService');
|
|
|
|
return hash.toString();
|
|
} catch (e) {
|
|
print('❌ Linux设备ID获取失败: $e');
|
|
KRLogUtil.kr_e('❌ Linux设备ID获取失败 - $e', tag: 'KRDeviceInfoService');
|
|
return '';
|
|
}
|
|
}
|
|
|
|
/// 获取或创建存储的设备ID
|
|
Future<String> _getOrCreateStoredDeviceId() async {
|
|
try {
|
|
const key = 'kr_device_unique_id';
|
|
final storage = KRSecureStorage();
|
|
|
|
String? storedId = await storage.kr_readData(key: key);
|
|
|
|
if (storedId == null || storedId.isEmpty) {
|
|
// 生成新的UUID
|
|
storedId = _generateUniqueId();
|
|
await storage.kr_saveData(key: key, value: storedId);
|
|
print('📱 生成新的设备ID: $storedId');
|
|
KRLogUtil.kr_i('📱 生成新的设备ID - $storedId', tag: 'KRDeviceInfoService');
|
|
} else {
|
|
print('📱 使用存储的设备ID: $storedId');
|
|
KRLogUtil.kr_i('📱 使用存储的设备ID - $storedId', tag: 'KRDeviceInfoService');
|
|
}
|
|
|
|
return storedId;
|
|
} catch (e) {
|
|
print('❌ 获取存储的设备ID失败: $e');
|
|
KRLogUtil.kr_e('❌ 获取存储的设备ID失败 - $e', tag: 'KRDeviceInfoService');
|
|
return _generateUniqueId();
|
|
}
|
|
}
|
|
|
|
/// 生成唯一ID
|
|
String _generateUniqueId() {
|
|
final timestamp = DateTime.now().millisecondsSinceEpoch.toString();
|
|
final random = DateTime.now().microsecondsSinceEpoch.toString();
|
|
final combined = '$timestamp-$random';
|
|
|
|
// 使用MD5生成唯一标识
|
|
final bytes = utf8.encode(combined);
|
|
final digest = md5.convert(bytes);
|
|
|
|
return digest.toString();
|
|
}
|
|
|
|
/// 获取设备详细信息
|
|
Future<Map<String, dynamic>> _getDeviceDetails() async {
|
|
try {
|
|
if (Platform.isAndroid) {
|
|
final androidInfo = await _deviceInfo.androidInfo;
|
|
return {
|
|
'platform': 'android',
|
|
'device': androidInfo.device,
|
|
'model': androidInfo.model,
|
|
'brand': androidInfo.brand,
|
|
'manufacturer': androidInfo.manufacturer,
|
|
'androidId': androidInfo.id,
|
|
'version': androidInfo.version.release,
|
|
'sdkInt': androidInfo.version.sdkInt,
|
|
};
|
|
} else if (Platform.isIOS) {
|
|
final iosInfo = await _deviceInfo.iosInfo;
|
|
return {
|
|
'platform': 'ios',
|
|
'name': iosInfo.name,
|
|
'model': iosInfo.model,
|
|
'systemName': iosInfo.systemName,
|
|
'systemVersion': iosInfo.systemVersion,
|
|
'identifierForVendor': iosInfo.identifierForVendor,
|
|
'isPhysicalDevice': iosInfo.isPhysicalDevice,
|
|
};
|
|
} else if (Platform.isMacOS) {
|
|
final macInfo = await _deviceInfo.macOsInfo;
|
|
return {
|
|
'platform': 'macos',
|
|
'computerName': macInfo.computerName,
|
|
'model': macInfo.model,
|
|
'hostName': macInfo.hostName,
|
|
'arch': macInfo.arch,
|
|
'systemGUID': macInfo.systemGUID,
|
|
};
|
|
} else if (Platform.isWindows) {
|
|
final windowsInfo = await _deviceInfo.windowsInfo;
|
|
return {
|
|
'platform': 'windows',
|
|
'computerName': windowsInfo.computerName,
|
|
'numberOfCores': windowsInfo.numberOfCores,
|
|
'systemMemoryInMegabytes': windowsInfo.systemMemoryInMegabytes,
|
|
};
|
|
} else if (Platform.isLinux) {
|
|
final linuxInfo = await _deviceInfo.linuxInfo;
|
|
return {
|
|
'platform': 'linux',
|
|
'name': linuxInfo.name,
|
|
'version': linuxInfo.version,
|
|
'id': linuxInfo.id,
|
|
'machineId': linuxInfo.machineId,
|
|
};
|
|
} else if (kIsWeb) {
|
|
final webInfo = await _deviceInfo.webBrowserInfo;
|
|
return {
|
|
'platform': 'web',
|
|
'browserName': webInfo.browserName.toString(),
|
|
'userAgent': webInfo.userAgent,
|
|
'vendor': webInfo.vendor,
|
|
};
|
|
}
|
|
|
|
return {
|
|
'platform': 'unknown',
|
|
};
|
|
} catch (e) {
|
|
print('❌ 获取设备详情失败: $e');
|
|
KRLogUtil.kr_e('❌ 获取设备详情失败 - $e', tag: 'KRDeviceInfoService');
|
|
return {
|
|
'platform': 'unknown',
|
|
'error': e.toString(),
|
|
};
|
|
}
|
|
}
|
|
|
|
/// 获取平台名称
|
|
String getPlatformName() {
|
|
if (Platform.isAndroid) return 'Android';
|
|
if (Platform.isIOS) return 'iOS';
|
|
if (Platform.isMacOS) return 'macOS';
|
|
if (Platform.isWindows) return 'Windows';
|
|
if (Platform.isLinux) return 'Linux';
|
|
if (kIsWeb) return 'Web';
|
|
return 'Unknown';
|
|
}
|
|
|
|
/// 获取设备型号
|
|
String getDeviceModel() {
|
|
if (_deviceDetails == null) return 'Unknown';
|
|
|
|
if (Platform.isAndroid) {
|
|
return '${_deviceDetails!['brand']} ${_deviceDetails!['model']}';
|
|
} else if (Platform.isIOS) {
|
|
return _deviceDetails!['model'] ?? 'Unknown';
|
|
} else if (Platform.isMacOS) {
|
|
return _deviceDetails!['model'] ?? 'Unknown';
|
|
}
|
|
|
|
return 'Unknown';
|
|
}
|
|
|
|
/// 获取操作系统版本
|
|
String getOSVersion() {
|
|
if (_deviceDetails == null) return 'Unknown';
|
|
|
|
if (Platform.isAndroid) {
|
|
return _deviceDetails!['version'] ?? 'Unknown';
|
|
} else if (Platform.isIOS) {
|
|
return _deviceDetails!['systemVersion'] ?? 'Unknown';
|
|
}
|
|
|
|
return 'Unknown';
|
|
}
|
|
|
|
/// 获取User-Agent信息
|
|
String getUserAgent() {
|
|
final platform = getPlatformName();
|
|
final model = getDeviceModel();
|
|
final osVersion = getOSVersion();
|
|
|
|
return 'HiVPN/1.0.0 ($platform; $model; $osVersion) Flutter';
|
|
}
|
|
}
|