客户端更新包
推送机制:上传后点「发布」,各客户端站点在下一次联网校验时会自动收到该更新
(随校验响应一起下发并签名),无需逐家通知。客户端管理员在「授权管理」页看到提示并一键下载。
同一产品同时只推送一个版本;发布新版本会自动下线旧版本。
客户端对接协议(与本文件同源,逐字对齐)
授权对象是单位;验证模式是域名 + 在线验证。客户端每次联网校验都要上报
当前访问域名 与 站点标识;域名不在授权清单内直接判为无效。
1. 请求
POST {授权服务器地址} ← 填站点根,如 https://license.example.com/api/verify
Headers:
Content-Type: application/json
X-License-Site: <站点标识>
Body:
{
"license_key": "WMS-XXXX-XXXX-XXXX-XXXX",
"site_id": "<客户端首启生成并固化的随机串>",
"site_name": "库房进领存管理系统",
"product": "wms",
"version": "1.0.0",
"domain": "wms.example.com",
"hostname": "srv-01",
"timestamp": 1759999999
}
2. 响应
{
"code": 0, ← 0 = 协议正常;非 0 = 协议级拒绝
"msg": "授权有效",
"data": {
"status": "valid", ← valid / expiring / expired / revoked / invalid
"writable": true,
"customer": "贵州某某公司",
"expire_at": "2027-09-28",
"days_left": 365,
"permanent": false,
"modules": ["*"],
"bound_domains": ["wms.example.com"],
"grace_hours": 72,
"server_time": "2026-09-28 16:00:00"
},
"signature": "<HMAC-SHA256 十六进制>"
}
3. 签名规范形(双方独立复现)
payload = {k: v for k, v in body.items() if k != "signature"}
canonical = json.dumps(payload, ensure_ascii=False, separators=(",", ":"), sort_keys=True)
signature = hmac_sha256_hex(key=sign_secret, msg=canonical.encode("utf-8"))
⚠️ 绝不对「收到的原始字节」直接签名:签名本身也是响应体字段时,服务端在写出字节之前
算不出覆盖自己的摘要,协议在数学上无解。
4. 协议级错误码
1001
缺少授权码
1004
缺少访问域名
1005
授权码不存在
1006
授权码不属于该产品
1500
授权中心内部错误
「已过期 / 已吊销 / 域名未授权」一律返回 code=0 +
data.status,这样客户端仍能拿到客户名与到期日一并展示,
客户自助排障体验好得多。
5. 更新自动推送
/api/verify 响应 data 内新增(仅授权可用时):
"update": {
"version": "1.1.0", ← 比客户端 version 新才下发;客户端据此显示「最新版本」
"kind": "patch", ← full=全量包 / patch=增量包(只含改动文件)
"from_version": "1.0.0", ← 增量包的基准版本;客户端基准不符会拒绝安装
"notes": "本次更新内容…",
"filename": "wms-client-patch-1.0.0-to-1.1.0.zip",
"size": 10485760, "sha256": "…",
"download": "/api/update/download?id=1"
} ← 随响应签名一起被覆盖,不可伪造
下载:GET {server}/api/update/download?id=1&license_key=WMS-…
独立检查:POST {server}/api/update/check 体:{license_key, product, version}
发布 = 推送:管理台「更新推送」页点「发布」,客户端下一次联网校验即自动收到、自动安装并重启,
无需客户替换任何文件。包内带 VERSION /
PACKAGE.json / PATCH.json 时版本号会自动识别。