开发文档 {{ tabNames[tab] }}
返回后台
主题
V2 授权验证系统采用"客户端采集 + 服务端判定"架构,支持 Windows / Android 双端,RSA-2048加密传输,JWT令牌认证

接入流程

1
初始化
调用 init.php 接口获取RSA公钥,用于后续数据加密传输。这是接入的第一步,必须先完成初始化才能进行后续操作。
2
采集机器码
采集设备机器码生成指纹。建议:Windows 使用设备UUID;Android 使用 Android ID。
3
激活授权
调用 activate.php 接口,使用授权码+机器码激活,成功后获取JWT令牌和刷新令牌,令牌有效期24小时。
4
心跳保活
调用 heartbeat.php 接口,固定每5秒发送一次心跳维持在线状态。连续15秒无心跳将被判定为离线。

统一响应格式

所有API接口返回统一的JSON格式,包含code、message/msg、data、timestamp字段
JSON
{ "code": 200, // 状态码,200表示成功 "message": "成功", // 消息说明 "msg": "成功", // 消息说明(兼容字段) "data": { ... }, // 业务数据 "timestamp": 1705123456 // 服务器时间戳 }

注意:message 和 msg 字段内容相同,为兼容不同版本客户端,建议优先使用 message 字段

POST
https://wlyz.ay0.cn/api/v2/init.php

获取RSA公钥和服务器配置,这是接入的第一步。

请求参数

响应示例

JSON复制
{ "code": 200, "message": "成功", "data": { "public_key": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhki...\n-----END PUBLIC KEY-----", "server_time": 1705123456, "heartbeat_interval": 5 } }
POST
https://wlyz.ay0.cn/api/v2/activate.php
data字段需要RSA公钥加密后Base64编码,app_key明文传输

请求参数(外层)

加密数据内容(data解密后)

fingerprint 只需要 machine_code 字段(明码字符串),不需要SHA256

fingerprint 对象结构

JSON复制
{ "machine_code": "设备机器码(明码字符串)" } // 示例(Windows): { "machine_code": "C5B1F6B2-5C7E-4AF2-9C1A-0F8B3A2D1E9A" }

device_info 对象结构

JSON复制
{ "platform": "windows", // 平台: windows / android "os_version": "Windows 10", // 操作系统版本 "is_virtual": false, // 是否虚拟机 "virtual_type": "" // 虚拟机类型: vmware/virtualbox/hyper-v 等 }

响应示例

JSON复制
{ "code": 200, "message": "激活成功", "data": { "token": "eyJhbGciOiJSUzI1NiJ9...", "refresh_token": "rf_abc123...", "expire_time": "2026-02-13 12:00:00", "remain_days": 30 } }
POST
https://wlyz.ay0.cn/api/v2/heartbeat.php
心跳间隔固定5秒,连续15秒无心跳判定离线

请求参数

响应示例

JSON复制
{ "code": 200, "data": { "status": "online", "server_time": 1705123456, "expire_time": "2026-02-13 12:00:00", "remain_seconds": 2592000, "cloud": { "vars": { "download_url": "https://example.com/download", "notice_text": "欢迎使用" }, "announcements": [ { "id": 1, "title": "系统维护通知", "content": "系统将于今晚22:00-24:00进行维护升级,期间可能影响正常使用,请提前保存工作。", "type": "notice", "is_popup": 1, "show_once": 1 }, { "id": 2, "title": "新版本发布", "content": "v2.1.0版本已发布,修复了若干已知问题,建议及时更新。", "type": "update", "is_popup": 0, "show_once": 1 } ], "latest_version": { "version": "2.0.0", "version_code": 200, "download_url": "https://...", "force_update": 0 }, "switches": { "feature_a": true, "maintenance": false } } } }

云控制数据说明

公告处理示例

公告数据在心跳响应的 cloud.announcements 数组中,客户端应根据 type 和 is_popup 字段进行相应处理
C#复制
// 已读公告ID列表(建议持久化存储到本地文件或注册表) private static HashSet<int> _readAnnouncementIds = new HashSet<int>(); private static bool _isFirstHeartbeat = true; // 处理心跳响应中的公告数据 if (response.data.cloud?.announcements != null) { foreach (var announcement in response.data.cloud.announcements) { int id = announcement.id; int showOnce = announcement.show_once; // 1=只显示一次, 0=每次启动显示 int isPopup = announcement.is_popup; // 判断是否需要显示 bool shouldShow = false; if (showOnce == 1) { // 只显示一次模式:检查是否已读 if (!_readAnnouncementIds.Contains(id)) { shouldShow = true; } } else { // 每次启动显示模式:只在首次心跳时显示 if (_isFirstHeartbeat) { shouldShow = true; } } if (shouldShow) { string typeText = announcement.type switch { "notice" => "通知", "warning" => "警告", "update" => "更新", "ad" => "广告", _ => "公告" }; if (isPopup == 1) { // 弹窗显示 MessageBox.Show( announcement.content, $"[{typeText}] {announcement.title}", MessageBoxButtons.OK, announcement.type == "warning" ? MessageBoxIcon.Warning : MessageBoxIcon.Information ); } else { // 添加到公告列表 AddAnnouncementToList(id, announcement.title, announcement.content, announcement.type); } // 标记为已读(只对show_once=1的公告记录) if (showOnce == 1) { _readAnnouncementIds.Add(id); SaveReadIds(); // 持久化保存 } } } _isFirstHeartbeat = false; }
Java (Android)复制
// 处理心跳响应中的公告数据 JSONArray announcements = cloudData.optJSONArray("announcements"); if (announcements != null) { for (int i = 0; i < announcements.length(); i++) { JSONObject announcement = announcements.getJSONObject(i); int id = announcement.getInt("id"); String title = announcement.getString("title"); String content = announcement.getString("content"); String type = announcement.getString("type"); int isPopup = announcement.getInt("is_popup"); // 如果需要弹窗显示 if (isPopup == 1) { // 在主线程显示弹窗 runOnUiThread(() -> { new AlertDialog.Builder(this) .setTitle(getTypeText(type) + " " + title) .setMessage(content) .setPositiveButton("确定", null) .show(); }); } else { // 添加到公告列表 addAnnouncementToList(id, title, content, type); } } } private String getTypeText(String type) { switch (type) { case "notice": return "📢 通知"; case "warning": return "⚠️ 警告"; case "update": return "🔄 更新"; case "ad": return "📺 广告"; default: return "📋 公告"; } }
POST
https://wlyz.ay0.cn/api/v2/refresh.php

JWT令牌24小时有效,过期后1小时内可刷新

请求参数

响应示例

JSON复制
{ "code": 200, "message": "刷新成功", "data": { "token": "eyJhbGciOiJSUzI1NiJ9...", "refresh_token": "rf_new123..." } }
POST
https://wlyz.ay0.cn/api/v2/trial.php
每个设备只能试用一次,data字段需RSA加密

请求参数(外层)

加密数据内容

响应示例

JSON复制
{ "code": 200, "message": "试用激活成功", "data": { "token": "eyJhbGciOiJSUzI1NiJ9...", "refresh_token": "rf_trial...", "expire_time": "2026-01-13 12:30:00", "remain_seconds": 1800, "is_trial": true } }

防重放攻击

所有请求需携带 timestamp + nonce + sign 三个参数

重要:签名算法只拼接参数的值(value),不包含参数名(key)
签名算法步骤复制
1. 将业务参数按key字母顺序排序 2. 只拼接参数的值(不是key=value格式) 3. 拼接字符串: timestamp + nonce + 参数值 + client_secret 4. 计算SHA-256哈希得到sign 示例(激活接口): 业务参数: app_key="SK_abc123", auth_code="AUTH001" 排序后: app_key, auth_code(按字母顺序) 拼接值: "SK_abc123" + "AUTH001" = "SK_abc123AUTH001" 最终字符串: "1705123456" + "randomnonce123" + "SK_abc123AUTH001" + "your_client_secret" sign = SHA256("1705123456randomnonce123SK_abc123AUTH001your_client_secret") 示例(心跳接口): 业务参数: token="eyJhbGci..." 最终字符串: timestamp + nonce + token值 + client_secret sign = SHA256("1705123456randomnonce123eyJhbGci...your_client_secret")

各接口签名参数

机器指纹采集

指纹容错:当硬件变更导致指纹匹配率≥80%时触发人工审核,低于80%则拒绝
以下是完整的SDK实现代码,可直接复制使用。代码中的API地址已自动替换为当前服务器地址。
{{ sdkLangs[tab] }}
{{ codes[tab] }}