接入 KT验证系统
用一次 HTTP 请求完成卡密验证。本页包含接口字段、响应格式、设备码规范与可运行示例。
快速开始
从生成第一张卡密,到客户端成功验证,只需要三个步骤。
登录后台生成卡密
在「生成卡密」页选择应用、有效期和设备上限。完整卡密只显示一次,请复制或下载 CSV。
客户端提交验证
把卡密、应用标识和稳定设备码提交到 /api/public/verify。
根据 valid 放行
响应中 data.valid === true 才进入主功能,否则提示原因并退出。
本系统采用在线卡密模式,客户端每次启动都应请求服务器验证,不能仅凭本地缓存永久放行。
核心概念
卡密
随机生成的授权凭证,保存时只存 HMAC 摘要和尾号。首次验证即激活,并开始计算有效期。
应用标识
app_code 用于隔离不同软件。卡密只能通过生成时所属应用的验证。
设备码
device_id 是客户端生成的稳定标识。系统只保存其 HMAC 摘要,用于限制一码多用。
授权状态
未使用、使用中、已过期、已停用。停用或到期后,下一次验证会立即返回失败。
验证接口
该接口无需登录或 API Key,卡密本身就是凭证。生产环境必须使用 HTTPS。
/api/public/verify
卡密过期、设备超限等结果使用 data.valid = false 表示。不要只检查 HTTP 状态码,必须读取响应体中的 valid 字段。
curl -X POST "https://your-domain.com/api/public/verify" \
-H "Content-Type: application/json" \
-d '{
"app_code": "demo-app",
"card_key": "KT-7F2M-9QXA-B4TN-H8WP-3C6D-2E9F-5J7K-AB",
"device_id": "web-4a3f0f2e-8b90-4c94-9d2e-8e72f3cda001",
"device_name": "Windows 11 / 我的电脑"
}'
请求参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
app_code | string | 是 | 应用标识,在后台「应用管理」中查看,例如 demo-app。 |
card_key | string | 是 | 用户输入的卡密,不区分大小写,可包含横线或空格。 |
device_id | string | 是 | 稳定设备码,长度 6-200,建议为 UUID 或硬件指纹哈希。同一设备必须保持不变。 |
device_name | string | 否 | 设备显示名,便于后台识别,例如 Windows 11 · 办公室电脑,最长 80 字符。 |
请求体必须是 application/json,最大 1 MB。接口按 IP 和卡密分别限流,超出后返回 HTTP 429。
响应格式
验证成功
{
"ok": true,
"data": {
"valid": true,
"code": "activated",
"message": "激活成功,授权已生效",
"activated_now": true,
"app": { "code": "demo-app", "name": "示例软件" },
"card": {
"masked_key": "KT-****-****-****-AB",
"activated_at": "2026-10-24T05:27:45Z",
"expires_at": "2026-10-31T05:27:45Z",
"permanent": false,
"duration_seconds": 604800,
"remaining_seconds": 604800,
"device_limit": 1,
"devices_bound": 1
},
"device": { "name": "Windows 11 / 我的电脑", "current": true }
}
}
首次成功验证时 code 为 activated;之后为 valid。永久卡密没有 expires_at,并返回 permanent: true。
验证失败
{
"ok": true,
"data": {
"valid": false,
"code": "device_limit",
"message": "该卡密已绑定 1 台设备,已达到上限",
"card": {
"masked_key": "KT-****-****-****-AB",
"device_limit": 1,
"devices_bound": 1
}
}
}
validboolean,唯一判断依据。为 true 时才能放行。code机器可读的结果码,见下方错误码表。message可直接展示给用户的中文说明。card卡密信息,失败时可能只包含部分字段。错误码
| code | HTTP | 含义 | 客户端处理建议 |
|---|---|---|---|
invalid_key | 200 | 卡密不存在、格式错误或不属于该应用。 | 提示检查卡密,不要自动重试。 |
expired | 200 | 卡密有效期已结束。 | 提示续费或获取新卡密。 |
disabled | 200 | 管理员已停用卡密。 | 提示联系发卡方。 |
device_limit | 200 | 已达到设备绑定上限。 | 提示联系管理员解绑。 |
app_disabled | 200 | 应用被管理员暂停验证。 | 提示稍后再试或联系管理员。 |
invalid_device | 400 | 设备码为空或格式不正确。 | 检查客户端设备码生成逻辑。 |
rate_limited | 429 | 请求过于频繁。 | 读取 Retry-After,指数退避。 |
internal_error | 500 | 服务器内部错误。 | 延迟重试,并通知管理员。 |
无法连接验证服务器时,应提示用户检查网络或稍后再试;不要在异常分支默认解锁。
设备码规范
设备码决定了「一码一机」是否可靠。它必须在同一台机器上保持稳定,并且不要包含可直接识别个人身份的信息。
✓ 推荐做法
- 桌面端组合主板序列号、系统盘序列号、CPU ID 后做 SHA-256。
- Web 端生成随机 UUID 并保存在
localStorage。 - 首次生成后写入配置文件或系统注册表,后续复用。
- 只提交哈希值或随机 ID,不提交原始硬件序列号。
× 避免做法
- 每次启动随机生成,导致每次都变成新设备。
- 直接使用 IP 地址或 MAC 地址(容易变化)。
- 直接提交用户名、手机号等个人信息。
- 把卡密写入可被轻易复制的明文配置后不再验证。
Python 示例
使用 requests 验证卡密。设备码会持久化到用户目录,避免每次启动都变化。
import hashlib
import json
import platform
import subprocess
import uuid
from pathlib import Path
import requests
API = "https://your-domain.com/api/public/verify"
APP_CODE = "demo-app"
DEVICE_FILE = Path.home() / ".myapp" / "device_id"
def get_device_id() -> str:
"""首次启动生成并保存,之后保持不变。"""
if DEVICE_FILE.exists():
return DEVICE_FILE.read_text(encoding="utf-8").strip()
seed = f"{platform.node()}-{uuid.getnode()}-{uuid.uuid4()}"
device_id = "pc-" + hashlib.sha256(seed.encode()).hexdigest()[:32]
DEVICE_FILE.parent.mkdir(parents=True, exist_ok=True)
DEVICE_FILE.write_text(device_id, encoding="utf-8")
return device_id
def verify(card_key: str) -> dict:
response = requests.post(
API,
json={
"app_code": APP_CODE,
"card_key": card_key.strip(),
"device_id": get_device_id(),
"device_name": f"{platform.system()} {platform.release()}",
},
timeout=8,
)
response.raise_for_status()
result = response.json()["data"]
if not result["valid"]:
raise RuntimeError(result["message"])
return result
if __name__ == "__main__":
try:
info = verify(input("请输入卡密: "))
print(f"授权有效,到期时间:{info['card'].get('expires_at') or '永久'}")
# 在这里启动你的主程序
except Exception as exc:
print(f"授权失败:{exc}")
input("按回车退出...")
raise SystemExit(1)
C# 示例
using System.Net.Http.Json;
public record VerifyRequest(
string app_code, string card_key, string device_id, string device_name);
public sealed class LicenseService
{
private static readonly HttpClient Http = new()
{
Timeout = TimeSpan.FromSeconds(8)
};
private const string Api =
"https://your-domain.com/api/public/verify";
public static async Task<bool> VerifyAsync(string cardKey)
{
var payload = new VerifyRequest(
"demo-app",
cardKey.Trim(),
DeviceId.Current, // 稳定的设备码
"Windows · 我的电脑");
var response = await Http.PostAsJsonAsync(Api, payload);
response.EnsureSuccessStatusCode();
var envelope = await response.Content
.ReadFromJsonAsync<ApiEnvelope>();
if (envelope?.Data is null || !envelope.Data.Valid)
{
MessageBox.Show(envelope?.Data?.Message ?? "验证失败");
return false;
}
return true;
}
}
C# 示例省略了 ApiEnvelope 数据模型定义,按上方 JSON 结构创建对应属性即可。
正确的放行逻辑
授权验证不是一个「提示框」,而是主功能的前置条件。所有受保护入口都应经过同一处判断。
读取本地设备码
提交卡密和设备码
仅 true 时继续
false 或异常则退出
- 01
只认
data.valid。不要因为 HTTP 200、响应存在或字段缺失就放行。 - 02
失败分支必须终止。验证失败后禁用主功能,而不是只弹一个可以关闭的提示。
- 03
保护关键逻辑。前端隐藏按钮不等于安全;服务端接口和核心数据也要校验授权。
- 04
定期复验。长时间运行的程序建议每隔数小时重新验证一次,以便停用即时生效。
部署说明
本机运行
# 双击 start.bat,或手动运行
python app.py --port 8765
# 允许局域网访问(注意 Windows 防火墙)
python app.py --lan --port 8765
# 首次启动默认管理员
# 账号:admin
# 密码:admin123456
环境变量
| 变量 | 默认值 | 说明 |
|---|---|---|
KEYGUARD_HOST | 127.0.0.1 | 监听地址。对外服务可设为 0.0.0.0。 |
KEYGUARD_PORT | 8765 | 监听端口。 |
KEYGUARD_DATA_DIR | ./data | 数据库与密钥目录。 |
KEYGUARD_ADMIN_USER | admin | 首次初始化时的管理员账号。 |
KEYGUARD_ADMIN_PASSWORD | admin123456 | 首次初始化时的管理员密码。 |
KEYGUARD_TRUST_PROXY | 0 | 位于反向代理后时设为 1,用于读取真实 IP。 |
KEYGUARD_SECURE_COOKIES | 0 | HTTPS 部署时设为 1,Cookie 仅通过 HTTPS 发送。 |
KEYGUARD_CORS_ORIGIN | 空 | 仅当浏览器端跨域调用验证接口时设置精确来源。 |
对外部署建议
- 01
使用 Nginx / Caddy 反向代理到
127.0.0.1:8765,并配置 HTTPS 证书。 - 02
设置
KEYGUARD_TRUST_PROXY=1和KEYGUARD_SECURE_COOKIES=1,否则登录 Cookie 与限流 IP 可能不正确。 - 03
备份
data/keyguard.db和data/secret.key。丢失密钥会导致所有卡密无法验证。 - 04
不要把
data/目录暴露为静态文件;建议仅开放 443 端口,后端 8765 不直接对公网开放。
如果沿用初始密码,任何知道默认值的人都能进入后台。登录后请前往「账号安全」修改。
安全建议
- 01
始终使用 HTTPS。卡密在请求体中传输,明文 HTTP 可能被截获。
- 02
不要在客户端硬编码管理员密码。管理后台只供管理员使用,客户端只调用验证接口。
- 03
设备码只做授权用途。保存 HMAC 摘要而非原始硬件信息,避免不必要的隐私风险。
- 04
限流与日志。系统默认对验证和登录有限流,建议定期查看验证日志中的异常 IP。
- 05
合规使用。仅将本系统用于合法的软件授权和数字内容保护,不得用于恶意软件或规避他人授权。
常见问题
关闭生成结果弹窗后,还能找回完整卡密吗?
不能。系统只保存卡密的 HMAC 摘要和尾号,完整卡密仅在生成成功时返回一次。请重新生成,或在关闭前复制 / 下载 CSV。
有效期从什么时候开始计算?
从用户首次成功验证并绑定设备时开始,不是从生成时间开始。未使用的卡密不会消耗时间。
用户换电脑了怎么办?
管理员在「卡密管理」中打开卡密详情,点击「解绑设备」。用户下次验证即可绑定新设备,已使用的有效期不会重置。
可以给不同软件发不同的卡密吗?
可以。在「应用管理」创建多个应用,每个应用有独立 app_code。卡密不能跨应用使用。
支持离线验证吗?
当前版本是在线卡密系统,需要客户端能够访问验证服务器。无法联网时不应默认放行。
怎么判断卡密是不是被多人共用?
把设备上限设为 1,系统会拒绝第二台设备。验证日志中可以看到不同设备尝试被拦截的记录。
生成第一张卡密,三分钟完成接入。
