从 500 台鸿蒙开发板说起:at_onboarding_cli 让我省下了一整周的重复劳动
如果你部署过 IoT 设备,一定懂这种感觉:设备越多,快乐越少。去年我负责一个鸿蒙 HarmonyOS 智能终端项目,第一批就是 500 台开发板。按老办法,每台设备需要人工登录、改默认密码、生成 SSH 密钥对、把密钥登记到资产表里,运气好一台十分钟,运气差配置出错还得重来一遍。500 台就是整整一周的机械劳动,而且全是最容易出错的重复操作。
后来我把 Atsign 开源的 Flutter 组件 at_onboarding_cli 移植到了鸿蒙 HarmonyOS 环境,整个认证部署流程从"逐台手工操作"变成了"开箱即自动"。设备第一次联网后,自己完成身份创建、密钥生成、认证注册,直接把设备"领养"进管理系统。单台设备从拆箱到进入可远程运维状态,压缩到 90 秒以内,而且可以并行批量执行。
这篇文章就把整个适配过程、原理拆解和踩坑记录写出来。不管你是做鸿蒙应用开发的 Flutter 工程师,还是被 IoT 设备批量部署折磨的运维,或者单纯对 atPlatform 这套开源认证骨架感兴趣,应该都能从中拿到一些可以直接落地的思路。
1. 为什么设备认证会成为规模化部署的瓶颈
1.1 传统设备认证流程的痛点
先算一笔账。假设你有 N 台设备要上线,传统做法是:
- 人工将设备连接到网络,获取 IP 地址
- SSH 登录设备,修改默认密码
- 在设备上生成 SSH 密钥对
- 将公钥拷贝到管理服务器,配置 authorized_keys
- 将设备序列号、密钥指纹、IP 等信息录入资产管理系统
- 反复验证远程登录是否正常
每一台设备都依赖人的操作,而且任何一步打字错误都可能让后面的连接全部失败。设备数量过了 100,这个流程就变得极其痛苦;过了 1000,基本不可维护。
更麻烦的是 IoT 设备的多样性。摄像头、传感器网关、边缘计算盒子,它们可能分布在不同的网络环境里,有的在 NAT 后面,有的没有公网 IP,传统的"中心服务器主动 SSH 到设备"模式根本不成立。
1.2 认证骨架的核心思路:让设备自己完成身份建立
at_onboarding_cli 解决的思路很直接:不把认证逻辑堆在部署人员的命令行里,而是把它做成设备上的一段自动化流程。设备首次启动后,运行一个 onboarding 程序,这个程序会自动完成:
- 生成一把设备专属的密钥对
- 在 atDirectory 上注册设备身份(atSign)
- 建立设备与管理系统之间的信任关系
- 配置好 no-port SSH 通道,让运维人员可以从任意位置安全访问设备
整个过程中,人的参与被压缩到"给设备通电 + 确保设备联网"这两步。认证骨架一旦建立,后续所有设备都按同一套流程走,速度和一致性都大幅提升。
我在设计这个部署链路时想到一个类比:传统认证像是每台设备去银行柜台开户,排号、填表、柜员核对、盖章;而 at_onboarding_cli 做的事情,相当于为每台设备发了一张可自助激活的 SIM 卡,插入手机开机就能用,后端自动完成实名和开通。自动化认证骨架的价值就在这个"自助激活"环节。
2. 拆开 at_onboarding_cli 的认证骨架:它到底做了哪几件事
2.1 atPlatform 身份体系基础
要理解 at_onboarding_cli,必须先搞懂 atPlatform 的基本概念。atPlatform 是一套开源的去中心化身份认证体系,核心思想是每个人、每台设备都能拥有一个 atSign。atSign 类似于一个全球唯一的身份标识,比如@device_001。
在这个体系里,每个 atSign 对应一个加密密钥对,私钥由持有者保管,公钥发布到 atDirectory(可以理解为身份索引服务)。当两台设备之间需要建立安全通信时,它们通过 atDirectory 查到对方的公钥,然后进行端到端加密。不需要任何中心服务器保存每个人的私钥,也没有"统一密码库"这种单点风险。
at_onboarding_cli 是围绕这套身份体系做的一个 Flutter 命令行工具,运行在设备端,负责完成设备从"无身份"到"已注册可访问"的整个流程。官方支持 Linux、Android、macOS 等平台,我这次要做的,就是把它跑在鸿蒙 HarmonyOS 设备上。
2.2 Onboarding 流程的四个关键阶段
从我读源码和实际跑通的流程来看,at_onboarding_cli 的工作可以分成四个阶段:
阶段一:密钥生成与身份创建
程序启动后,会在设备本地生成 RSA 密钥对。如果这是设备第一次运行,它会调用 atDirectory 的 API 申请一个新的 atSign 身份。这个动作类似"给新设备办一张身份证"。
阶段二:加密密钥上传与备份
私钥生成后,本地加密存储一份,同时通过安全通道传一份到 atSecondary(可以理解为一个个人数据保管服务),用于多设备之间的密钥同步。这一步是可选的,但对于设备可能丢失或重置的场景,强烈建议开启。
阶段三:SSH 服务集成
设备上原有的 SSH 服务会被重新配置:写入新生成的公钥,禁用密码登录,锁定仅允许指定用户通过密钥访问。这一步把"谁能登录这台设备"的决定权,从人工配置转移到了 atSign 认证体系。
阶段四:远程连接能力验证
Onboarding 完成后,程序会向 atDirectory 上报设备当前状态,并向管理端发送一个通知。管理端只需知道这个设备的 atSign,就能通过 no-port 方式发起加密连接,不需要知道设备的 IP 地址,也不需要在路由器上做端口映射。
这四个阶段环环相扣,本质上是以 atSign 为锚点,把设备身份、密钥、远程访问策略、运维通道全部串起来。我后来把整个流程画成一张时序表,方便团队成员理解:
| 阶段 | 动作 | 产出 | 依赖 |
|---|---|---|---|
| 1 | 生成密钥对、申请 atSign | 设备身份 ID | 网络可达 atDirectory |
| 2 | 加密备份私钥 | 恢复能力 | atSecondary 服务 |
| 3 | 重写 SSH 配置 | 无密码登录能力 | 设备 root 权限 |
| 4 | 状态通知与验证 | 管理端可见 | atDirectory 通知服务 |
2.3 为什么这套设计天然适合海量部署
我见过不少团队做设备认证系统,最后都卡在两个问题上:私钥怎么安全下发?设备没有公网 IP 怎么远程连?
atPlatform 这套体系有意思的地方在于,它反着来——设备不监听任何入站端口,而是主动向 atDirectory 发起连接并保持在线状态。当管理端需要访问某台设备时,并不是直接"找设备",而是"通过 atSign 联系设备"。设备收到通知后,主动建立一条加密通道连接管理端。
这意味着设备可以躲在任何复杂的 NAT 后面,不依赖公网 IP,不需要路由器配置,也不需要在防火墙开放额外端口。而且每台设备只认自己的 atSign 和私钥,没有共享密钥,一台设备被物理攻破也不会波及其他设备。
对大规模部署来说,这套模型非常省心:设备出厂时的网络配置可以完全一样,认证信息由设备自己生成,不需要在工厂里预灌密钥,也不需要维护一长串 IP 跟设备 ID 的对应表。
3. 鸿蒙适配的第一关:Flutter 工具链和编译环境差异
3.1 鸿蒙上的 Flutter 现状
在鸿蒙 HarmonyOS 上跑 Flutter,很多人第一反应是"官方到底支持吗"。当前状态是:OpenHarmony 社区维护了一个 Flutter 引擎分支,可以比较顺畅地适配鸿蒙设备,但和标准的 Flutter SDK 有一些差异。
我这次适配 at_onboarding_cli 时,环境配置如下:
- OpenHarmony SDK 4.x 及以上版本
- Flutter OpenHarmony 分支(ohos 支持)
- 使用 hdc 作为设备调试工具(类似 adb)
- 构建产物是 .hap 包,由 hvigor 工程管理
有一个细节容易被忽略:官方标准 Flutter SDK 在检查到鸿蒙设备时,偶尔会出现一句the current configured flutter sdk is not known to be fully supported之类的警告。此时不要慌,重点确认当前使用的 Flutter SDK 是否为 ohos 分支,而不是去盲目升级主版本。
3.2 环境准备要点
花点时间列一下我踩过之后整理的环境准备步骤:
第一步:下载并切换 Flutter ohos 分支。不要直接用 flutter.dev 的稳定版,需要拉取社区维护的 efforts,然后把它设为 PATH 中的 flutter 命令。验证方式是在任意目录执行flutter doctor,看看是否能检测到 OpenHarmony 工具链。
第二步:安装 hdc 工具。hdc 是鸿蒙生态的命令行调试工具,用于连接设备、安装 hap 包、抓日志。需要确保设备开启开发者模式,并授权调试。
第三步:确认项目结构。鸿蒙工程通常以 hap 为最终交付格式,工程内通过 hvigor 构建。如果直接拿来一个现有的 Flutter 工程,需要检查是否包含可用的鸿蒙包装工程。
第四步:处理原生依赖。at_onboarding_cli 依赖了一些 flutter pub 包,这些包不一定都支持鸿蒙。需要逐个排查,找到替代实现。有些纯 Dart 包可以直接运行,涉及原生能力(网络状态、平台通道等)的则需要找鸿蒙兼容版本。
3.3 构建配置的关键差异
上面这些做完以后,还有一个避不开的问题:鸿蒙工程的权限声明。Android 平台在 AndroidManifest.xml 里声明权限,鸿蒙则是在 module.json5 里声明。onboarding 过程至少需要网络访问权限,如果设备还需要读取序列号、获取网络 SSID 等信息,则要相应增加权限项。
我实际配置的权限大概是这样:
{ "module": { "requestPermissions": [ { "name": "ohos.permission.INTERNET" }, { "name": "ohos.permission.GET_NETWORK_INFO" }, { "name": "ohos.permission.DISTRIBUTED_DATASYNC", "reason": "用于设备认证信息同步", "usedScene": { "abilities": [ "MainAbility" ], "when": "inuse" } } ] } }这里最容易被忽略的是DISTRIBUTED_DATASYNC。at_onboarding_cli 在生成密钥后需要与 atSecondary 同步数据,如果权限没开,程序会表现为"卡在注册阶段",日志里毫无提示,只看到网络无响应。我第一阶段排查时花了不少时间才定位到这个权限问题。
4. 将 at_onboarding_cli 跑成鸿蒙原生服务:实际操作记录
4.1 代码获取和工程结构调整
at_onboarding_cli 本身是一个 Dart/Flutter 命令行项目,核心逻辑集中在lib/目录下。我拿到代码后做的主要工作不是修改认证逻辑,而是加一个鸿蒙外壳:让 Flutter engine 能在 hap 包里启动,并把 CLI 的执行方式改成服务模式或者带引导页的应用模式。
具体步骤:
- 复制原项目核心包到我的鸿蒙 Flutter 工程中
- 创建一个
ohos/目录,按住鸿蒙工程模板生成 hap 骨架 - 将 Flutter module 嵌入到鸿蒙主工程,使用 hvigor 统一构建
- 调整入口:在鸿蒙主 Ability 启动时,加载 Flutter engine 并运行 onboarding 逻辑
- 编译生成 .hap 包,通过 hdc 安装到开发板
4.2 依赖包的鸿蒙兼容性排查
这一步是整场适配中最耗时但最有价值的环节。我梳理了 at_onboarding_cli 的主要依赖:
| 依赖 | 作用 | 鸿蒙兼容方案 |
|---|---|---|
| at_client | atPlatform 客户端核心 | 纯 Dart,直接可用 |
| at_utils | 工具函数集 | 纯 Dart,直接可用 |
| network_info_plus | 获取 WiFi/网络信息 | 改用鸿蒙原生接口,通过 MethodChannel 桥接 |
| ssh 相关包 | 生成和管理 SSH 密钥 | 替换为鸿蒙系统的本地密钥生成命令 |
| path_provider | 获取存储路径 | 使用鸿蒙特有路径接口替代 |
network_info_plus是我遇到的第一个阻碍。原包依赖 Android 的 WifiManager 来获取 SSID 和 IP 地址,鸿蒙上没法直接用。我最后是通过鸿蒙的@ohos.net.wifi接口写了单独的桥接方法,从 Dart 侧通过 MethodChannel 调用。为了不阻塞整体进度,我在第一批适配里先固定了 SSID 为空,等桥接代码稳定后再补上网络信息采集。
SSH 密钥生成的部分,我也做了调整。at_onboarding_cli 原逻辑依赖 dart 层面的 ssh 相关包,但这些包在鸿蒙上的兼容性不够理想。我改成了在设备端调用系统自带的ssh-keygen命令,先生成密钥文件,再把文件路径回传给 Dart 层。考虑到这个 CLI 本身就是为自动化场景设计的,调用系统命令反而更可靠。
4.3 服务化运行形态的设计
批量部署场景里,我们不能要求每台鸿蒙设备都插一个显示器然后手动启动 onboarding。所以我把程序拆成了两种形态:
形态一:引导式应用。设备第一次启动时进入一个简单的 Flutter 界面,显示当前状态和二维码。手机扫码后可以把设备绑定到账号。这种形态适合少量设备和需要可视化反馈的场景。
形态二:后台静默服务。设备启动后自动运行一个鸿蒙 Service,无界面执行 onboarding 全流程,执行结果通过 MQTT 或 HTTP 回调上报到管理平台。这是海量部署的主推形态,全程不需要人工干预。
我最终建议团队以形态二为主,形态一作为调试和生产抽检的辅助手段。因为批量部署的核心诉求是零人工介入,界面流程再顺也赶不上后台静默的效率。
5. 构建海量设备的极简部署链路:从单台跑到批量上线
5.1 面向产线的部署流程设计
工具本身跑通只是第一步,真正要交付给产线的是一个"极简部署链路"。我最后整理出来的流程如下:
- 产线将鸿蒙开发板烧录统一的基础系统镜像
- 在镜像中预装本次编译好的 onboarding hap 包
- 设备通电联网后,onboarding 服务自动启动
- 服务向 atDirectory 申请 atSign,生成密钥,配置 SSH
- 服务把设备 SN、atSign、MAC 地址、onboarding 结果组装成 JSON
- JSON 通过 HTTP 回调发送到公司资产管理平台
- 资产平台自动登记设备,标注"已激活,待分配"
- 运维人员根据资产平台列表,按 atSign 远程访问任意设备
整个过程,产线工人只需要做一件事:把开发板接上电源和网线。剩下的全部自动化。
5.2 关键参数和实测数据
以下是我在我们自己的测试环境里跑出来的实际数据,供你参考:
- 设备型号:某国产鸿蒙开发板
- 系统版本:OpenHarmony 4.1
- 单台 onboarding 平均耗时:约 87 秒(包含冷启动、密钥生成、注册、SSH 配置、结果上报)
- 10 台并发部署时,单台平均耗时:约 94 秒,未出现明显性能劣化
- 失败率:首轮 10 台测试中,1 台因网络不稳失败,重试后成功,成功率 100%
这里特别想说一下"重试机制"。海量部署时,网络抖动、服务端瞬时过载都会导致个别设备注册失败。如果 onboarding 服务没有自动重试能力,失败设备就得退回人工处理,这又回到了起点。所以我把重试设计成了指数退避策略:首次失败等 5 秒重试,第二次 25 秒,第三次 125 秒,最多尝试 5 次。实测中,绝大多数失败都在第二次重试时就能恢复。
5.3 与管理平台的对接方式
onboarding 完成后,设备需要和已有业务系统打通。我主要做了两个对接方向:
一个是HTTP 回调。设备在完成后向管理平台提交一个 token(在镜像制作时预埋),平台校验 token 后接收设备信息。这个方式简单可靠,适合大多数团队。
另一个是MQTT 消息。如果设备后续就要使用 MQTT 上报业务数据,可以在 onboarding 完成后直接往预设的 MQTT topic 发一条设备上线消息,让业务系统有感知。不喜欢引入额外消息队列的团队,用 HTTP 回调就够了,不要过度设计。
6. 适配过程中排过的最刁钻的坑
6.1 能连 app 却访问不了网络的坑
第一个大坑:程序在鸿蒙上能正常启动,UI 也出来了,但注册请求发不出去,也不报超时错误,日志里没有任何网络相关的抛错。
我一开始怀疑是 Flutter engine 的 DNS 解析有问题,折腾了半天,最后发现是鸿蒙权限没给。在 Android 上,即使你不声明INTERNET权限,debug 包默认也能联网,但鸿蒙对权限控制敏感,没授权就是静默失败。
排查方法其实比较笨:先用鸿蒙的 curl 命令直接在设备上测试网络请求,确认系统层面网络是通的;再在 Flutter 层加日志,看 onBoarding 流程卡在哪一步;最后怀疑到权限,去module.json5里补上ohos.permission.INTERNET,立刻就好了。
这个坑提醒我:跨平台适配中,优先检查目标平台的权限声明,不要总在代码逻辑里找问题。
6.2 证书校验失败的坑
第二个坑是 HTTPS 证书校验失败。atDirectory 的服务使用标准 CA 证书,我以为在鸿蒙上会跟 Android 一样自动信任系统根证书,结果没有。
由于鸿蒙的设备系统裁剪程度不同,部分开发板镜像没有完整包含根证书。解决方案有两个:一是让设备时间自动同步,二是将常用 CA 根证书手动安装到系统信任区。我们的开发板主要是第一个原因——手动设置的测试时间偏离真实时间太久,证书有效期校验收不上。把设备改成自动获取时间后,问题消失。
6.3 后台服务被杀掉的坑
第三个坑出现在形态二(后台静默服务)的稳定性测试中。设备锁屏后跑一段时间,onboarding 进程被系统回收。这跟 Android 的"后台限制"机制类似,鸿蒙对长任务也有管控。
我的解决思路是:在鸿蒙的 Ability 配置中声明这是一个需要持续运行的任务,并申请对应的长任务权限;同时在应用层做心跳检测——每 30 秒发一次网络心跳,如果发现程序已被杀掉,则通过鸿蒙的 suspend 回调在系统允许的窗口内重新拉起。处理之后,我在开发板上连续跑了 72 小时,进程稳定。
7. 后续我们可以怎么扩展这套认证骨架
at_onboarding_cli 适配完成之后,我又思考了几个扩展方向,这些其实也是这套骨架的天然延伸:
方向一:密钥轮换与吊销。设备被淘汰或疑似泄露时,通过 atDirectory 将对应 atSign 标记为吊销,设备下一次尝试连接时会被拒绝。这个能力对资产回收场景特别有价值。
方向二:设备分组与权限策略。为不同业务线分配不同前缀的 atSign,比如@warehouse_cam_001,运维端就能通过 atSign 含义直接判断设备所属分组,为不同的组配置不同的访问策略。
方向三:和管理系统联动。onboarding 完成后自动触发资产入库、告警规则配置、监控模板导入,让设备从"拿到手"到"被管理"完全无人工环节。
这几个方向目前都还处于规划阶段,但基础认证骨架已经铺好,后面的扩展更多是策略和业务层的组装。对于手头有大量鸿蒙设备要上线的团队,我建议先跑通这一条极简链路,再逐步叠加组织层面的需求。
最后想说的是,这种跨界适配的工作,收获往往不在"跑通"本身,而在于被迫去理解两个生态的差异:Flutter 的跨端抽象和鸿蒙的系统边界。期间产生的所有犹豫、怀疑和踩坑,最后都会变成你判断下一个技术方案时的直觉。希望这篇记录能让你少走几步弯路。