激活认证 Quick Start
本文档面向需要接入 Licence Server 的第三方软件客户端,说明如何通过激活服务完成软件认证。
当前支持两种接入方式:
- 方式一:扫码激活
- 方式二:手动下发激活码
基础信息
服务地址
下文统一使用 {BASE_URL} 表示注册器服务地址。
示例:
http://127.0.0.1:1280所有认证接口统一使用以下路由前缀:
/sansi/register/api/v1完整请求地址格式:
{BASE_URL}/sansi/register/api/v1/{resource}数据格式
- 请求体:
application/json - 时间戳:Unix 秒级时间戳,除非特别说明
- 激活码展示:服务端返回的激活码为不带横杠的字符串,客户端可按 4 位一组自行格式化展示
已支持 softName
softName 必须使用服务端已配置的软件名称,当前支持:
| softName |
|---|
cyber-hub |
starriver |
easyplay |
v3pro |
v3pro-arm |
quasar |
vizohub |
plc |
hulk |
激活类型和默认有效期
| 首字母 | code | 说明 | 扫码激活默认有效期 | 手动下发有效期 | 限制 |
|---|---|---|---|---|---|
T | trialActivation | 试用激活 | 当前时间 + 1 个月 | 以 validityPeriod 为准 | 有效期不能超过 30 天 |
E | emergencyActivation | 应急激活 | 当前时间 + 7 天 | 以 validityPeriod 为准 | 有效期不能超过 7 天 |
F | formallyActivation | 正式激活 | 当前时间 + 99 年 | 以 validityPeriod 为准 | 需要合同号 |
H | homeActivation | 家庭激活(预留) | 当前时间 + 99 年 | 以 validityPeriod 为准 | 需要合同号 |
X | customizedActivation | 定制激活(预留) | 当前时间 + 99 年 | 以 validityPeriod 为准 | 需要合同号 |
说明:
- 扫码激活的默认有效期只在移动端确认激活时没有传
deadline时生效。 - 扫码激活如果传了
deadline,服务端使用传入的 Unix 秒级时间戳作为最终有效期。 - 手动下发创建中间码时,服务端不会按激活类型自动补默认有效期,必须显式传
validityPeriod。 - 如果
activeType为空或不是上述枚举,服务端会按trialActivation处理;建议客户端不要依赖这个兜底逻辑。
设备标识
第三方客户端需要提供稳定的设备标识。服务端当前参与激活码计算的字段为:
| 字段 | 说明 | 是否必填 |
|---|---|---|
softName | 软件名称,需要和服务端配置枚举一致 | 是 |
contractNumber | 合同号。试用/应急激活可为空,其他类型必填 | 按激活类型 |
sysUUID | 系统 UUID | 扫码激活必填;手动下发时和 hardNumber 不能同时为空 |
hardNumber | 硬盘/硬件序列号(注意虚拟机) | 扫码激活必填;手动下发时和 sysUUID 不能同时为空 |
computerName | 设备名称 | 建议提供 |
客户端应保证 sysUUID、hardNumber 的获取逻辑稳定。这里别整花活,设备指纹一变,激活码自然就对不上。
方式一:扫码激活(推荐使用)
扫码激活适用于客户端可以展示二维码,并由企业微信/移动端扫码确认的场景。
流程
https://ccs-pro.sansi.io/cyberhub/deploy.html#软件激活
- 第三方客户端采集设备信息。
- 第三方客户端按服务端约定生成 RSA 加密二维码。
- 移动端扫码后调用服务端,将二维码状态置为
activing。 - 移动端选择激活类型、使用场景并确认激活。
- 服务端生成最终激活码,并将二维码状态置为
actived。 - 第三方客户端轮询二维码状态,拿到最终激活码。
- 第三方客户端保存激活码,并在本地或通过服务端校验激活状态。
1. 生成二维码内容
二维码中的核心字段是 code,它是 RSA 加密后的 Base64 字符串。
明文格式必须按以下顺序拼接,字段之间使用英文逗号:
{softName},{contractNumber},{sysUUID},{timestamp},{computerName},{hardNumber},{deadline},{deviceCount}示例明文:
cyber-hub,HT202606150001,550E8400-E29B-41D4-A716-446655440000,1781518800,DESKTOP-001,DISK-SN-001,1765288356,25556加密要求:
- 算法:RSA
- Padding:PKCS#1 v1.5
- 公钥:使用与服务端
server/key/private.txt配套的公钥 - 输出:Base64 字符串
二维码内容建议为 JSON:
{
"code": "BASE64_RSA_CIPHERTEXT"
}公钥
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCu6eFNmpyEYZfpAZMqI7FEnt6h
N1BqwPZAHaHh+P6u4GBkxZGES1clAo3YjqEgL+68JLrAc9rZRaq95LPHIk/SJ688
hzXD6RTtsW3EKo6HTTKWhzAXQAtaCAsigYq40ewZzkk6bTDpy4ourJVnlLqLFMTk
6RJsrK5E7sxiGuxWqQIDAQAB
-----END PUBLIC KEY-----当前移动端扫码页面会解析二维码 JSON,并读取其中的 code 字段。
2. 客户端轮询激活状态
第三方客户端展示二维码后,应轮询状态接口。
POST /sansi/register/api/v1/active/code/status请求示例:
curl -X POST "{BASE_URL}/sansi/register/api/v1/active/code/status" \
-H "Content-Type: application/json" \
-d '{
"code": "BASE64_RSA_CIPHERTEXT"
}'响应示例:
{
"status": "unknown",
"activeCode": ""
}status 说明:
| status | 说明 | 客户端处理 |
|---|---|---|
unknown | 二维码尚未被扫描 | 继续轮询 |
activing | 已扫码,用户尚未确认激活 | 提示用户在移动端确认 |
actived | 已激活成功 | 读取 activeCode 并保存 |
建议轮询间隔 2-5 秒,并设置总超时时间。二维码明文中的 timestamp 太旧会导致激活失败,服务端当前会按时间判断二维码是否过期。
3. 移动端扫码通知服务端
移动端扫码后调用该接口,把二维码状态置为 activing。
POST /sansi/register/api/v1/active/code/scan请求示例:
curl -X POST "{BASE_URL}/sansi/register/api/v1/active/code/scan" \
-H "Content-Type: application/json" \
-d '{
"code": "BASE64_RSA_CIPHERTEXT",
"user": ""
}'成功响应:
{
"status": "activing",
"softName": "cyber-hub",
"contractNumber": "HT202606150001"
}4. 移动端确认激活
POST /sansi/register/api/v1/message?activeType={activeType}activeType 决定许可证首字母和默认有效期。请求体可选传 deadline,传入后会覆盖该激活类型的默认有效期。
请求示例:
curl -X POST "{BASE_URL}/sansi/register/api/v1/message?activeType=formallyActivation" \
-H "Content-Type: application/json" \
-d '{
"code": "BASE64_RSA_CIPHERTEXT",
"user": "张三",
"usageScenarios": ["traffic"],
"remark": "项目现场激活"
}'成功响应:
{
"status": "F123456789ABCDEF"
}注意:这里响应字段名叫 status,但值实际是最终激活码。这个命名挺别扭,对接时别被它骗了。
默认有效期: | --- | --- | --- | | trialActivation | 当前时间 + 1 个月 | 使用传入的 Unix 秒级时间戳 | | emergencyActivation | 当前时间 + 7 天 | 使用传入的 Unix 秒级时间戳 | | formallyActivation | 当前时间 + 99 年 | 使用传入的 Unix 秒级时间戳 | | homeActivation | 当前时间 + 99 年 | 使用传入的 Unix 秒级时间戳 | | customizedActivation | 当前时间 + 99 年 | 使用传入的 Unix 秒级时间戳 |
5. 无网离线校验(客户端本地校验)
无网离线校验属于扫码激活链路的本地校验部分,用于客户端拿到最终 activeCode 后,在后续无法访问注册器服务时自行判断许可证是否匹配本机。
客户端本地校验的核心是:使用本机设备标识按同一套 SHA-256 规则重新生成激活码,并与扫码激活得到的 activeCode 对比。
5.1 离线激活码生成规则
最终 activeCode 使用以下字段按固定顺序直接拼接,不使用分隔符:
{softName}{contractNumber}{hardNumber}{sysUUID}字段说明:
| 字段 | 说明 |
|---|---|
softName | 软件名称,需要和注册器配置一致 |
contractNumber | 合同号。试用/应急激活可为空;为空时按空字符串参与拼接 |
hardNumber | 硬盘/硬件序列号。为空时按空字符串参与拼接 |
sysUUID | 系统 UUID。为空时按空字符串参与拼接 |
生成步骤:
- 使用 UTF-8 将拼接后的明文转为字节。
- 计算 SHA-256。
- 取 SHA-256 结果的前 8 个字节,并转为大写十六进制字符串。
- 用激活类型首字母替换第 1 个字符。
伪代码:
plain = softName + contractNumber + hardNumber + sysUUID
sha256First8Hex = UPPERCASE(HEX(SHA256(UTF8(plain))[0:8]))
activeCode = activeTypePrefix + SUBSTRING(sha256First8Hex, 1, 15)其中 SHA256(...)[0:8] 表示取 SHA-256 结果的前 8 个字节;SUBSTRING(sha256First8Hex, 1, 15) 表示从下标 1 开始取 15 个字符。最终 activeCode 长度为 16 位。
激活类型首字母沿用前文表格:
| activeType | prefix |
|---|---|
trialActivation | T |
emergencyActivation | E |
formallyActivation | F |
homeActivation | H |
customizedActivation | X |
示例:
softName = cyber-hub
contractNumber = HT202606150001
hardNumber = DISK-SN-001
sysUUID = 550E8400-E29B-41D4-A716-446655440000
activeType = formallyActivation拼接明文:
cyber-hubHT202606150001DISK-SN-001550E8400-E29B-41D4-A716-446655440000SHA-256 结果:
77D7C571E51E28566884D210713C7BBA1AF0408F94388136E6C29CE49ACE5F97取前 8 个字节转大写 HEX,即 77D7C571E51E2856,并将首字母替换为 F:
F7D7C571E51E2856展示时可格式化为:
F7D7-C571-E51E-28565.2 客户端离线校验步骤
客户端启动或用户输入激活码后,按以下方式本地校验:
- 读取本机
softName、contractNumber、hardNumber、sysUUID。 - 将输入的
activeCode去掉横杠和空格,并转成大写。 - 取
activeCode第 1 位作为激活类型首字母。 - 按上面的 SHA-256 规则重新生成本机
expectedActiveCode。 - 比较
expectedActiveCode == activeCode。
注意:
activeCode本身只绑定softName、contractNumber、hardNumber、sysUUID和激活类型首字母,不包含有效期。- 有效期、设备数量、使用场景、备注等授权信息需要和
activeCode一起保存,例如保存在本地授权文件中。 - 设备标识必须和生成激活码时完全一致。大小写、空格、序列号来源变化都可能导致 SHA-256 结果不同。
- 纯离线校验不依赖
GET /sansi/register/api/v1/active/code/validate
6. 常见错误
| HTTP 状态 | code | message | 说明 |
|---|---|---|---|
| 400 | 400 | 具体错误信息 | 参数错误、解密失败或业务校验失败 |
| 418 | 418 | Unix 时间戳 | 二维码过期,需要重新生成 |
| 420 | 420 | 合同号不存在,请输入完整合同号 | 合同号校验失败 |
方式二:手动下发激活码
手动下发适用于客户端无法方便扫码,或由管理端先创建授权码,再交给客户端完成绑定的场景。
这条链路分两段:
- 管理端创建
intermediateCode。 - 第三方客户端提交
intermediateCode和本机设备信息,换取最终activeCode。
1. 管理端创建中间码
POST /sansi/register/api/v1/active/code/create权限要求:
- 请求头
X-User-Roles必须包含licence:admin
请求示例:
curl -X POST "{BASE_URL}/sansi/register/api/v1/active/code/create" \
-H "Content-Type: application/json" \
-H "X-User-Roles: licence:admin" \
-d '{
"softName": "cyber-hub",
"contractNumber": "HT202606150001",
"user": "张三",
"usageScenarios": ["traffic"],
"remark": "手动下发",
"activeType": "formallyActivation",
"validityPeriod": 4917254399
}'请求字段:
| 字段 | 类型 | 说明 | 是否必填 |
|---|---|---|---|
softName | string | 软件名称 | 是 |
contractNumber | string | 合同号。试用/应急可为空 | 按激活类型 |
user | string | 申请/操作用户 | 建议填写 |
usageScenarios | string[] | 使用场景 | 建议填写 |
remark | string | 备注 | 建议填写 |
activeType | string | 激活类型 | 是 |
validityPeriod | number | 激活有效期,Unix 秒级时间戳 | 是 |
成功响应:
{
"intermediateCode": "F1A2B3C4",
"expirationTime": 1781520600
}intermediateCode 不是最终激活码,它是 30 分钟有效的一次性中间码。客户端兑换成功后,服务端会删除该中间码并写入最终激活记录。
2. 第三方客户端兑换最终激活码
第三方客户端收到 intermediateCode 后,提交本机设备信息进行绑定。
POST /sansi/register/api/v1/active/code/validate请求示例:
curl -X POST "{BASE_URL}/sansi/register/api/v1/active/code/validate" \
-H "Content-Type: application/json" \
-d '{
"intermediateCode": "F1A2B3C4",
"sysUUID": "550E8400-E29B-41D4-A716-446655440000",
"computerName": "DESKTOP-001",
"hardNumber": "DISK-SN-001"
}'请求字段:
| 字段 | 类型 | 说明 | 是否必填 |
|---|---|---|---|
intermediateCode | string | 管理端创建的中间码 | 是 |
sysUUID | string | 系统 UUID | 和 hardNumber 不能同时为空 |
computerName | string | 设备名称 | 建议填写 |
hardNumber | string | 硬盘/硬件序列号 | 和 sysUUID 不能同时为空 |
成功响应:
{
"activeCode": "F123456789ABCDEF",
"validityPeriod": 4917254399
}客户端应保存:
activeCodevalidityPeriod- 当前设备标识快照,例如
sysUUID、hardNumber
3. 校验最终激活码
如果客户端需要在线校验最终激活码,可调用:
GET /sansi/register/api/v1/active/code/validate?activeCode={activeCode}请求示例:
curl "{BASE_URL}/sansi/register/api/v1/active/code/validate?activeCode=F123456789ABCDEF"成功响应:
{
"activeCode": "F123456789ABCDEF",
"validityPeriod": 4917254399
}失败响应示例:
{
"code": "400",
"message": "激活码不存在",
"status": false
}服务端会自动去掉传入激活码中的横杠并转成大写,因此 F123-4567-89AB-CDEF 和 F123456789ABCDEF 都可以传。
4. 手动下发常见错误
| 场景 | message | 处理方式 |
|---|---|---|
| 无管理员权限创建中间码 | 用户权限不足,请联系管理员 | 检查 X-User-Roles |
| 合同号为空 | 合同号不能为空 | 正式/家庭/定制激活必须传合同号 |
| 合同号无效 | 合同号不存在或无效 | 检查合同号或合同服务 |
| 试用期超过 30 天 | 试用期不能大于30天 | 缩短 validityPeriod |
| 应急期超过 7 天 | 应急期不能大于7天 | 缩短 validityPeriod |
| 中间码重复创建 | 同一个合同号上一个二维码还未使用 | 等 30 分钟过期或删除旧中间码 |
| 中间码为空 | 二维码不能为空 | 检查客户端输入 |
| 中间码已用/不存在 | 二维码不存在或已被使用 | 重新创建中间码 |
| 设备标识缺失 | 硬件序列号和系统UUID不能同时为空 | 至少上传一个稳定设备标识 |
| 中间码过期 | 二维码已过期 | 重新创建中间码 |
推荐客户端处理策略
扫码激活
- 生成二维码时使用当前时间戳,避免旧二维码被重复使用。
- 二维码展示后立即开始轮询
/sansi/register/api/v1/active/code/status。 - 轮询到
actived后保存activeCode,停止轮询。 - 超时、过期或解密失败时重新生成二维码。
手动下发
- 客户端输入或扫描
intermediateCode。 - 调用
POST /sansi/register/api/v1/active/code/validate兑换最终activeCode。 - 兑换成功后不要继续使用
intermediateCode,它已经失效。 - 本地保存最终
activeCode和有效期,启动时先做本地有效期判断,需要强校验时再请求服务端。
接入检查清单
- 已拿到注册器服务地址
{BASE_URL}。 - 扫码激活需拿到服务端私钥配套公钥,并确认 RSA PKCS#1 v1.5 加密结果可被服务端解密。
softName与服务端枚举一致。- 正式/家庭/定制激活传入有效合同号。
- 客户端设备标识稳定,至少
sysUUID或hardNumber不为空。 - 客户端正确处理
unknown、activing、actived三种状态。 - 客户端区分
intermediateCode和最终activeCode,别把中间码当最终授权凭据。