You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

112 lines
4.8 KiB

import 'dart:async';
import 'package:device_plugin_interface/device_plugin_interface.dart';
import 'package:get/get.dart';
import '../data/models/user_Info.dart';
import 'bes/bes_device_auth.dart';
import 'bes/bes_device_plugin.dart';
import 'device_bind_registry.dart';
import 'device_hub.dart';
import 'holder/holder_device_plugin.dart';
import 'smartcar/smartcar_device_auth.dart';
import 'smartcar/smartcar_device_plugin.dart';
/// 确权结论(厂商无关)。
///
/// ⚠️ [unknown] 的语义是**放行**,不是"失败"。判不出来时放行是刻意选的失败方向:
/// 判严的代价是合法用户一断网就用不了自己的设备;判宽只是未登记设备离线时
/// 能用一会儿,联网第一次确权就会被拦下。
enum DeviceAuthResult {
/// 白名单命中 / 服务端确认 / 已在 User.devices 里
authorized,
/// 服务端明确拒绝(这台 MAC 没登记)
denied,
/// 未登录 / 断网 / 拿不到 MAC —— **放行**
unknown,
}
/// 设备确权的业务层入口(登出重置、解绑清白名单、补校当前设备)。
///
/// 确权本身是各厂商插件在连接时就地做的(恒玄见 [BesDeviceAuth]),这里只把
/// 需要从业务层触发的几个动作聚在一起,业务层不用知道是哪家的实现。
class DeviceAuth {
DeviceAuth._();
/// 登出:换账号后同一台设备要重新登记到新账号名下。**白名单不清**。
static void reset() {
BesDeviceAuth.reset();
SmartcarDeviceAuth.reset();
// 换账号后同一台设备要重新登记到新账号名下(服务端会接管)
DeviceBindRegistry.reset();
}
/// 解绑:移出白名单,下次连上重新走服务端确权。
///
/// 两家都调:调用方只有一个 MAC,不知道它属于哪个厂商,
/// 而各家白名单是按 MAC 存的、互不相干,多清一次无副作用。
static Future<void> forget(String mac) async {
await BesDeviceAuth.forget(mac);
await SmartcarDeviceAuth.forget(mac);
}
/// 【调试/售后】清空白名单
static Future<void> clearWhitelist() async {
await BesDeviceAuth.clearWhitelist();
await SmartcarDeviceAuth.clearWhitelist();
}
/// 补校当前连着的、需要确权的设备(顺带完成绑定登记)。
/// 「先连设备后登录」时连接事件里的登记会因未登录跳过,这里补一次。
///
/// ⚠️ **每接一种会绑定的设备都要在这里加一个分支**。漏掉的后果不是「少一层
/// 保险」,而是那种设备**永远补不上登记**:连接时若还没登录就跳过了,
/// 而此后没有任何别的路径会再触发。支架(holder)原先就漏在这儿 ——
/// 冷启动时它被动回连的速度常常快过登录态恢复,于是设备连着、
/// 「设备管理」页却一直是空的,且不报任何错。
static Future<void> verifyConnected() async {
final hub = DeviceHub.to;
for (final s in hub.sessions.toList()) {
if (!s.supports(DeviceCapability.macAddress)) continue;
final p = hub.plugin(s.vendor);
if (p is BesDevicePlugin) {
await p.link.auth?.verifyConnected();
} else if (p is HolderDevicePlugin) {
// 支架没有单独的确权类,登记就在服务自己身上(本进程内已成功的会去重)
await p.link.bindConnectedToServer();
} else if (p is SmartcarDevicePlugin) {
// Smartcar 的 MAC 来自广播,两端都拿得到(不像恒玄 iOS 只有 UUID)
await SmartcarDeviceAuth.verify(
mac: s.info.macAddress,
name: s.info.name,
);
}
}
}
static Worker? _loginWorker;
/// App 启动时挂一次:登录态一变化就对当前连着的设备补报一次绑定登记。
///
/// ⚠️ 不能只靠「连接时登记」+「进设备管理页补一次」这两条:设备是被动回连的,
/// 冷启动时它上线常常早于登录态恢复,那次登记直接被「未登录」跳过;
/// 而用户不一定会去点设备管理页 —— 于是设备连着、服务端却没有绑定记录,
/// 「我的设备」空着还不报错。登录完成后补这一次,把这个时序洞堵上。
static void bindLoginRetry() {
_loginWorker?.dispose();
_lastLoggedIn = User.isLoggedIn();
_loginWorker = ever<int>(User.refreshTick, (_) {
final now = User.isLoggedIn();
final was = _lastLoggedIn;
_lastLoggedIn = now;
// 只在**登录态从无到有**那一次补报。refreshTick 在用户信息每次刷新时都会 +1,
// 不收敛的话会对「服务端拒绝」的设备反复打 binddevice(成功的那些有 _bound 去重,
// 失败的没有)——那正是还没导入 MAC 的设备最常见的状态。
if (now && !was) unawaited(verifyConnected());
});
}
static bool _lastLoggedIn = false;
}