# v5 API 接口调用方式、请求/响应示例与排障说明见 `docs/api-usage.md`。 客户端 API 使用 JSON 请求体,机器码要求为 32 个字符。成功验证返回 `licenseTicket`,票据绑定当前机器码。 `/api/card/*` 的 `verify`、`heartbeat`、`unbind`、`devices` 均支持软件接入密钥 `appKey`(8-64 位字母、数字、下划线或连字符)。平台默认开启强制接入校验(平台设置 `cardAppKeyEnforce=1`),此时请求必须携带正确的 `appKey`,服务端据此强制卡密归属(空间 + 软件)一致,拒绝跨空间、跨软件的卡密验证;服务端会为缺失密钥的软件自动补发密钥。平台关闭强制校验(`cardAppKeyEnforce=0`)后回到按软件可选:仅对已生成密钥的软件校验。管理侧:`GET /api/admin/software` 返回各软件 `AccessKey` 与 `appKeyEnforced`;`POST /api/admin/software/{id}/access-key` 请求体 `{"action":"generate"}` 生成或重置密钥(响应 `accessKey` 返回新密钥)、`{"action":"clear"}` 停用校验(仅平台关闭强制校验时可用),仅空间管理员可调用。详见 `docs/api-usage.md` 的「软件接入密钥(appKey)」。 | 方法 | 路径 | 作用 | | --- | --- | --- | | POST | `/api/card/verify` | 激活或登录卡密(可选 `appKey`) | | POST | `/api/card/heartbeat` | 更新设备心跳(可选 `appKey`) | | POST | `/api/card/unbind` | 当前机器自助解绑(可选 `appKey`) | | POST | `/api/card/devices` | 查询本卡设备池(可选 `appKey`) | | GET | `/api/site/info` | 匿名读取站点标题与 ICP 备案号 | | GET | `/api/support/info` | 匿名读取平台公开客服联系方式 | | GET | `/api/support/chat` | 匿名拉取访客会话消息(按 `token` 与 `sinceId`) | | POST | `/api/support/chat/send` | 匿名发送访客咨询消息 | | GET | `/api/admin/account` | 查询当前管理员账户 | | PUT | `/api/admin/account` | 修改管理员账户和密码 | | POST | `/api/admin/ticket` | 根据有效卡密和机器码申请 RSA 票据 | | GET | `/api/admin/packages` | 查询套餐 | | POST | `/api/admin/packages` | 创建卡类型和套餐策略 | | GET | `/api/admin/software` | 查询本空间软件列表(含接入密钥 `AccessKey` 与平台 `appKeyEnforced`) | | POST | `/api/admin/software` | 新建软件(平台强制校验时随建随生成密钥) | | PUT | `/api/admin/software/{id}` | 重命名软件(同步更新关联卡类型) | | POST | `/api/admin/software/{id}/access-key` | 生成/重置或停用软件接入密钥(`action` 取 `generate` 或 `clear`;强制校验时 `clear` 返回 400) | | POST | `/api/admin/software/{id}/bind` | 设置该软件的卡类型绑定(完整期望集):`packageIds` 为要绑定的卡类型 ID 列表,未包含的卡类型会从该软件解绑;同一卡类型可同时绑定多个软件 | | POST | `/api/admin/software/{id}/restore` | 恢复已停用的软件 | | DELETE | `/api/admin/software/{id}` | 删除软件(默认软件或仍有关联卡类型时拒绝) | | POST | `/api/admin/cards/batch` | 按软件名称和卡类型批量生成卡密 | | POST | `/api/admin/cards/batch-delete` | 批量删除未激活卡密 | | GET | `/api/admin/cards` | 分页查询卡密 | | POST | `/api/admin/cards/{id}/freeze` | 冻结卡密 | | POST | `/api/admin/cards/{id}/unfreeze` | 解冻卡密 | | PUT | `/api/admin/packages/{id}` | 修改套餐设备策略 | | PUT | `/api/admin/cards/{id}/device-limit` | 修改单卡覆盖策略 | | GET | `/api/admin/cards/{id}/devices` | 查询卡密设备池 | | GET | `/api/admin/devices` | 跨卡密查询设备列表(支持分页与筛选) | | GET | `/api/admin/devices/export` | 按筛选条件导出设备列表 Excel | | POST | `/api/admin/devices/batch-kick` | 批量解绑设备(最多 500 台) | | POST | `/api/admin/devices/{id}/kick` | 后台解绑设备 | | GET | `/api/admin/dashboard/online` | 查询在线设备 | | GET | `/api/admin/protection/builds` | 查询本空间受保护构建 | | POST | `/api/admin/protection/builds` | 注册或更新受保护构建 | | POST | `/api/admin/protection/builds/{id}/revoke` | 吊销受保护构建 | | GET | `/api/admin/shell/help` | 分页查询本空间求助工单 | | GET | `/api/admin/shell/help/{id}` | 查看工单详情(含空间与平台回复) | | PUT | `/api/admin/shell/help/{id}` | 编辑工单状态与空间回复 | | DELETE | `/api/admin/shell/help/{id}` | 删除工单 | | POST | `/api/admin/shell/help/batch-delete` | 批量删除工单(单次最多 200 条) | | POST | `/api/admin/shell/help/batch-reply` | 批量回复工单(仅待处理生效) | | POST | `/api/admin/shell/help/{id}/escalate` | 一键转交总后台 | | GET | `/api/admin/shell/help/export` | 导出工单 Excel | | GET | `/api/plat/shell/help` | 平台分页查询已转交工单 | | POST | `/api/plat/shell/help/{id}/reply` | 平台回复工单(仅空间管理员可见) | | GET | `/api/plat/support/guest/conversations` | 平台查询匿名访客会话列表 | | GET | `/api/plat/support/guest/conversations/{token}` | 平台读取访客会话消息 | | POST | `/api/plat/support/guest/conversations/{token}/send` | 平台回复访客会话 | | GET | `/api/admin/banrules` | 分页查询本空间封禁规则(机器码/卡密/IP) | | POST | `/api/admin/banrules` | 添加封禁规则 | | DELETE | `/api/admin/banrules/{id}` | 删除本空间封禁规则 | | GET | `/api/plat/banrules` | 平台分页查询全局封禁规则 | | POST | `/api/plat/banrules` | 平台添加全局封禁规则 | | DELETE | `/api/plat/banrules/{id}` | 平台删除全局封禁规则 | | GET | `/api/admin/guard/blacklist` | 查询本空间 IP 黑名单(含平台自动封禁记录) | | POST | `/api/admin/guard/blacklist` | 添加 IP 到黑名单(永久) | | DELETE | `/api/admin/guard/blacklist/{id}` | 删除单条黑名单记录 | | POST | `/api/admin/guard/blacklist/batch-delete` | 批量删除黑名单记录(单次最多 200 条) | | GET | `/api/admin/guard/blacklist/export` | 导出黑名单 Excel(可选 `ids` 参数) | | GET | `/api/admin/notify/settings` | 查询本空间预警与登录验证配置 | | PUT | `/api/admin/notify/settings` | 保存预警渠道、预警事件与登录验证配置 | | POST | `/api/admin/notify/test` | 按已启用渠道发送测试预警 | | POST | `/api/admin/login` | 管理员登录(开启二次验证时返回 `need2fa`) | | POST | `/api/admin/login/2fa` | 提交登录二次验证码完成登录 | | POST | `/api/admin/login/2fa/resend` | 重新发送登录验证码(60 秒冷却) | | GET | `/api/admin/users` | 查询本管理后台账号(含用户类型名称 `RoleName`) | | POST | `/api/admin/users` | 新建后台账号(`role` 为用户类型 Key,默认 `operator`) | | PUT | `/api/admin/users/{id}` | 修改账号用户名、状态与用户类型 | | POST | `/api/admin/users/{id}/reset-password` | 重置账号密码 | | POST | `/api/admin/users/{id}/email-code` | 发送邮箱变更验证码 | | PUT | `/api/admin/users/{id}/profile` | 修改账号资料 | | GET | `/api/admin/roles/types` | 查询本空间用户类型列表(含账号数与启用模块数) | | POST | `/api/admin/roles/types` | 新建用户类型(名称 2-20 字符,Key 由服务端生成) | | PUT | `/api/admin/roles/types/{id}` | 重命名用户类型(管理员类型不可改名) | | DELETE | `/api/admin/roles/types/{id}` | 删除用户类型(内置类型或名下仍有账号时拒绝) | | GET | `/api/admin/roles/permissions?role={key}` | 查询某用户类型可访问的模块 | | PUT | `/api/admin/roles/permissions` | 保存用户类型的模块权限(`{"role":"","enabled":[""]}`) | | GET | `/api/plat/account` | 查询当前平台账号(含角色 `role`、`roleName` 与 `disabledModules`) | | PUT | `/api/plat/account` | 修改当前平台账号用户名与密码 | | GET | `/api/plat/users` | 平台查询全部平台管理员(含用户类型名称 `RoleName`) | | POST | `/api/plat/users` | 新建平台管理员(`role` 为用户类型 Key,默认 `operator`) | | PUT | `/api/plat/users/{id}` | 修改平台管理员用户名、状态与用户类型 | | POST | `/api/plat/users/{id}/reset-password` | 重置平台管理员密码 | | GET | `/api/plat/roles/types` | 查询平台用户类型列表(含账号数与启用模块数) | | POST | `/api/plat/roles/types` | 新建平台用户类型(名称 2-20 字符,Key 由服务端生成) | | PUT | `/api/plat/roles/types/{id}` | 重命名平台用户类型(管理员类型不可改名) | | DELETE | `/api/plat/roles/types/{id}` | 删除平台用户类型(内置类型或名下仍有账号时拒绝) | | GET | `/api/plat/roles/permissions?role={key}` | 查询某平台用户类型可访问的模块 | | PUT | `/api/plat/roles/permissions` | 保存平台用户类型的模块权限(`{"role":"","enabled":[""]}`) | 用户与权限接口仅空间管理员可调用。用户类型保存在 `TenantRoles` 表,内置 `admin`(固定全部权限,不可改)与 `operator`(可配置),自定义类型 Key 形如 `r` 加 8 位十六进制。模块 Key 为 `software`、`cards`、`devices`、`points`、`share`、`support`、`guard`、`system`;对某类型未启用的模块,该类型账号访问对应接口时返回 403,后台菜单同步隐藏。 平台账号与权限接口仅平台管理员角色(`PlatformAdmins.Role='admin'`)可调用,停用自己或修改自己的用户类型会被拒绝,系统始终保留至少一个启用的管理员。平台用户类型保存在 `PlatformRoles` 表,内置 `admin`(固定全部权限,不可改)与 `operator`(可配置),自定义类型 Key 形如 `r` 加 8 位十六进制。平台模块 Key 为 `overview`、`tenants`、`finance`、`verify`、`support`、`settings`、`guard`、`accounts`;对某类型未启用的模块,该类型账号访问对应接口时返回 403,总控台菜单同步隐藏。`GET /api/plat/account` 返回 `role`、`roleName` 与 `disabledModules` 供前端按权限渲染菜单。 管理 API 可使用 `X-Admin-Key` 配置密钥进行服务端调用;管理后台登录后使用 PHP Session,并为写操作携带 `X-CSRF-Token`。批量出卡接口支持 `packageId`,也支持通过 `softwareName` 与 `packageName` 自动匹配套餐。接口只在响应中返回一次明文卡密,数据库仅保存 SHA-256 哈希。 `POST /api/admin/cards/batch-delete` 请求体为 `{"ids": [1,2,3]}`(单次最多 200 个)。仅删除 `ActivatedAt` 为空(未激活)的卡密,并连带清理其绑定日志与设备记录;只要批次中包含已激活卡密则整批拒绝,不删除任何记录。该接口需登录 Session 且携带 `X-CSRF-Token`。 创建或更新套餐时,`durationDays` 范围为 1-36500,`maxDevices` 范围为 1-100,`rebindDeductDays` 和 `extraPenaltyDays` 范围为 0-30,`overflowPolicy` 取值为 0、1 或 2。 账户更新请求字段为 `username`、`currentPassword`、`newPassword` 和 `confirmPassword`。账户名允许 3-50 个 ASCII 字母、数字、下划线、点号或连字符;新密码可留空以仅修改账户名,填写时长度为 8-128 个字符。 求助工单接口的约束:工单状态取值 1(待处理)或 2(已处理);回复内容长度 1-2000 字。批量回复仅对“待处理”工单生效并自动置为已处理。转交接口仅接受“待处理”工单,重复转交会被拦截;工单转交后平台可回复,平台回复内容仅对空间管理员可见,受保护程序无法读取。导出接口按当前筛选条件返回 xlsx 文件。 封禁规则接口的约束:`type` 取值 `machine`(32 位十六进制,服务端统一大写)、`card`(输入明文,服务端只存 SHA-256 哈希并返回打码文本)或 `ip`(IPv4/IPv6);`note` 备选。命中防护规则的机器码、卡密或 IP 调用 `/api/card/verify`、`/api/card/heartbeat`、`/api/card/unbind`、`/api/card/devices` 时返回 403,响应体为 `{"ok":false,"msg":"已被封禁(XX命中防护规则)","code":"banned_machine|banned_card|banned_ip","kick":true}`,每次命中规则计数 +1。机器码与卡密规则按内容全局匹配;IP 规则对空间级规则按卡密归属空间生效,平台全局规则对全部请求生效。空间删除仅限本空间规则,平台接口仅操作全局规则(`TenantId=0`)。 IP 黑名单接口写入 `IpGuardBan` 表,与平台自动封禁同机制同表:添加为永久封禁(`ExpireAt=NULL`),同一 IP 重复添加会覆盖原因与到期时间;删除/批量删除复用手动解封并写入防护日志。命中黑名单的 IP 在入口 `preflight` 即被拒绝(403),与「防护规则」的 IP 命中拦截相互独立。 预警与登录验证接口的约束:`PUT /api/admin/notify/settings` 仅管理员可调用(操作员返回 403),请求字段包括 `notifyEmailEnabled`、`notifyEmailTo`、`notifyWechatEnabled`、`notifyWeworkCorpId`、`notifyWeworkSecret`、`notifyWeworkAgentId`、`notifyWeworkToUser`、`notifyEvents`(数组或逗号分隔,取值 `security|login|service|card`)、`login2faEnabled`、`login2faChannel`(`email` 或 `wechat`)、`login2faEmail`;邮箱需为有效地址,AgentId 需为数字,Secret 留空表示保持原值且不回显。`POST /api/admin/notify/test` 仅管理员可调用,返回 `result`,其中 `email`/`wechat` 为对应渠道是否发送成功,`emailReady`/`wechatReady` 为配置是否就绪,忽略事件开关。 登录二次验证流程:开启后 `POST /api/admin/login` 在账号密码与图形验证码校验通过时返回 `{"ok":true,"need2fa":true,"channel":"email|wechat","target":"打码后的接收地址","resendIn":60}`,此时不建立登录会话;随后 `POST /api/admin/login/2fa` 提交 `{"code":"6 位验证码"}`,校验通过才写入 Session 并返回 `csrfToken`。验证码有效期 10 分钟,连续失败 5 次或过期需重新登录;`POST /api/admin/login/2fa/resend` 重新发送验证码,同一会话 60 秒内重复请求返回 429 与剩余等待秒数。 公开站点信息接口 `GET /api/site/info` 无需鉴权,返回平台总控台「平台设置」中的 `siteTitle` 与 `icpNumber`,供宣传首页页脚展示备案信息。`icpNumber` 为空时返回空字符串,首页回退为占位文案;响应不缓存。 公开客服接口 `GET /api/support/info` 无需鉴权,返回平台总控台配置的客服联系方式,供宣传首页右下角「在线客服」悬浮入口展示。平台关闭客服中心时返回 `{"ok":true,"enabled":false}`;启用时返回 `enabled`、`phone`、`qq`、`email`、`wechat`、`qr`、`workingHours` 与 `intro`,其中 `qr` 为同源二维码地址(`/api/support/qr/{token}`)或外部图片 URL,响应不缓存。 首页匿名在线咨询基于 `SupportGuestChatMessages` 表,会话身份是访客浏览器生成的 32 位十六进制 `token`(保存在 localStorage)。`GET /api/support/chat` 需携带合法 `token`,返回该会话自 `sinceId` 之后的消息并标记客服消息已读;`POST /api/support/chat/send` 请求体为 `{"token","content","nickname"?}`,`content` 长度 1-2000 字,`nickname` 可选且不超过 50 字,同一 `token` 每分钟最多发送 15 条(超出返回 429),客服离线时仍可留言。平台关闭客服中心时两个接口分别返回 `enabled:false` 或 400 `客服中心当前未开放`。平台回复接口需平台登录会话与 `X-CSRF-Token`,`content` 长度 1-5000 字。 ## RSA 票据申请 请求头:Session 登录请求需要 `X-CSRF-Token`;服务端调用可使用 `X-Admin-Key`。 请求体: ```json { "cardKey": "<卡密明文>", "machineCode": "<32位机器码>" } ``` 成功响应包含 `licenseTicket`、`expireUnix`、`remainDays` 和 `packageName`。卡密必须处于已激活、有效且未过期状态。服务端仅返回签名票据,私钥保留在服务端。 ## 受保护程序密钥释放 受保护 EXE 由保护工具生成:原始程序以一次性 AES-256-GCM 密钥加密,该载荷密钥使用平台 RSA 公钥以 OAEP(SHA-1) 封装。保护工具通过管理接口把封装密钥与构建清单上报服务端,服务端在验签成功时使用私钥解封并下发。 ### 注册构建 请求头:Session 登录请求需要 `X-CSRF-Token`;服务端调用可使用 `X-Admin-Key`。 请求体字段: | 字段 | 类型 | 说明 | | --- | --- | --- | | `buildGuid` | string | 构建标识,36 位 UUID | | `name` | string | 产品名称,1-100 字符 | | `version` | string | 版本号,1-40 字符 | | `targetKind` | int | 目标类型:1 .NET Framework,2 .NET 5+,3 原生 | | `arch` | string | 目标架构:`x86`、`x64` 或 `any` | | `manifest` | object | 构建清单,含 `policy.offlineGraceDays` 等策略 | | `wrappedKey` | string | Base64 的 RSA-OAEP(SHA-1) 封装载荷密钥 | | `plainHash` | string | 原始程序 SHA-256 十六进制摘要 | | `keyAlgo` | string | 可选,默认 `RSA-OAEP-SHA1` | | `productId` | int | 可选,关联的软件产品 ID | 同一空间下相同 `buildGuid` 重复提交按更新处理;`buildGuid` 已被其他空间占用时拒绝。 ### 在验签中释放密钥 `POST /api/card/verify` 请求体在 `cardKey`、`machineCode`、`deviceName` 之外可携带 `buildId`(构建标识)。当卡密验签成功且构建有效、空间启用时,成功响应追加: | 字段 | 类型 | 说明 | | --- | --- | --- | | `payloadKey` | string | Base64 的明文载荷密钥 | | `payloadKeyExpireUnix` | long | 载荷密钥到期 Unix 秒(与卡密到期一致) | | `offlineGrant` | string | 离线授权票据,格式 `base64(payload).base64(rsa-sha256 签名)` | | `offlineExpireUnix` | long | 离线授权到期 Unix 秒 | | `payloadKeyError` | string | 构建无效或被吊销时返回,且不下发 `payloadKey` | 离线授权票据载荷字段:`v`(版本)、`bid`(构建标识)、`ch`(卡密 SHA-256)、`mc`(机器码)、`k`(Base64 载荷密钥)、`exp`(授权到期 Unix 秒)、`off`(离线授权到期 Unix 秒)、`ts`(签发 Unix 秒)。`off` 取「当前时间 + 清单策略 `offlineGraceDays`」与 `exp` 的较小值。票据可用平台 `public.pem` 独立验签。 每次释放都会向 `PayloadKeyReleases` 写入审计记录,包含构建、卡密、空间、机器码、来源地址与渠道。 ### 吊销构建 `POST /api/admin/protection/builds/{id}/revoke` 将构建状态置为已吊销。吊销后该构建的验签响应不再下发 `payloadKey`,离线票据需在 `off` 到期后失效。