从安装包到授权生效,发生了什么

客户端通过 HTTPS 调用公开授权 API;签名 token 缓存在本地,之后每次启动可离线验签。只有需要感知吊销或 token 过期时才联网。

桌面客户端
license_key + 设备指纹
HTTPS →
UnionUser API
校验、激活、签发 token
→
PostgreSQL
授权、设备、审计

可选:自托管时,中间件与数据库部署在你自己的环境。

三条路径

接入只需要想清楚三个动作

1. 首次激活

用户输入授权码,客户端上报设备指纹,服务端原子占用名额并返回签名 token。

POST /api/v1/license/activate

2. 启动校验

每次启动先离线验签;token 临近过期或需要感知吊销时,再调用 verify 续验。

POST /api/v1/license/verify

3. 自动更新

把更新框架的 feed 指向发布源;需要门控时附带 license_key,产物本身另有签名。

GET /releases/{product_slug}/*
设备指纹

稳定、不可逆、不侵犯隐私

设备指纹只用于区分设备,不用于识别个人。建议读取系统原生机器标识,哈希后上报,并在隐私政策中说明。

  • Windows:注册表 MachineGuid(HKLM\SOFTWARE\Microsoft\Cryptography)
  • macOS:ioreg 的 IOPlatformUUID
  • Linux:/etc/machine-id
  • 统一哈希:sha256("app:" + raw).slice(0, 32),避免泄露原值
fingerprint.js示例
import { createHash } from 'node:crypto';
import { execSync } from 'node:child_process';

const raw = process.platform === 'win32'
  ? execSync('reg query "HKLM\\SOFTWARE\\Microsoft\\Cryptography" /v MachineGuid').toString()
  : execSync('cat /etc/machine-id').toString();

const deviceId = 'wm-' + createHash('sha256')
  .update('app:' + raw.trim()).digest('hex').slice(0, 32);
错误处理

把错误码映射成用户能懂的动作

Code含义建议动作
ACTIVATION_LIMIT设备名额已满引导客户在门户解绑旧设备
LICENSE_EXPIRED授权已到期展示续费入口,保留只读功能
LICENSE_SUSPENDED授权被暂停提示联系管理员,不自动重试
LICENSE_REVOKED授权被吊销停止解锁付费功能
DEVICE_HELD设备被占用/锁定提供解绑或稍后重试
SEAT_PAUSED席位暂停提示扩容或移除成员
RATE_LIMITED请求过于频繁指数退避重试
什么时候需要联网? token 的 exp 到期、权益发生变更、需要感知吊销、或新增设备时。其余时间,离线验签即可。