对接文档

这套文档对应当前站点上的接口,管理后台改了设置(比如自动授权、巡查间隔)这里会跟着变。签名的规矩只有一条:除 sign 外的所有参数,按键名升序拼成 k=v 用 & 连起来,拿应用私钥做 RSA-SHA256 签名。

一、接入流程

1. 在用户中心创建应用,拿到 应用ID(appid)、API密钥(apikey)、RSA 私钥。
2. 程序调用 auth.apply 传入唯一识别码,后台会收到一条待授权记录。
3. 管理员或代理审核,选到期时间(不选就是永久),点授权。
4. 调 auth.file 或在后台下载识别文件,文件名是随机生成的,放到站点根目录。
5. 授权站每 5 分钟访问一次这个文件做巡查,取消授权或到期会远程把它吊销。

二、公共参数

参数说明
action接口名,比如 auth.apply
appid应用ID,用户中心应用详情里能复制
apikeyAPI密钥,跟应用ID配对使用
timestamp当前时间戳(秒),和服务器相差超过 300 秒会被拒
nonce随机串,同一个应用内不能重复,防重放
sign签名,见下面的规则

请求方式 GET 或 POST 都支持,接口地址:https://sx5.ravarj.cn/api.php

三、签名规则

1. 把所有要传的参数(含公共参数,不含 sign)放进一个数组。
2. 按参数名升序排序(ksort)。
3. 拼成 a=1&b=2&c=3 的形式,注意值是原始值,不要先 urlencode。
4. 用应用私钥对这个字符串做 RSA-SHA256 签名,结果 base64 后放进 sign。
5. 服务端拿应用公钥验签,通过才处理业务。

<?php
// 不用 SDK 的话,自己拼也行,重点是签名规则
$params = array(
    'action'    => 'auth.apply',
    'appid'     => 'zy1a2b3c4d5e6f7g',
    'apikey'    => '你的API密钥',
    'timestamp' => time(),
    'nonce'     => bin2hex(random_bytes(8)),
    'code'      => 'MAC-8C1645A9B2D1',
    'domain'    => 'https://demo.com',
);

// 待签串:去掉 sign,其余按键名升序拼 k=v&k2=v2
unset($params['sign']);
ksort($params);
$pairs = array();
foreach ($params as $k => $v) {
    $pairs[] = $k . '=' . $v;
}
$signStr = implode('&', $pairs);

$pri = file_get_contents(__DIR__ . '/app_private.pem');
openssl_sign($signStr, $raw, $pri, OPENSSL_ALGO_SHA256);
$params['sign'] = base64_encode($raw);

$url = 'https://auth.demo.com/api.php?' . http_build_query($params);
$res = json_decode(file_get_contents($url), true);
if ($res['code'] === 0) {
    print_r($res['data']);
} else {
    echo '出错:' . $res['code'] . ' ' . $res['msg'];
}

四、接口一览

action作用额外参数返回要点
auth.apply提交授权申请code(必填,唯一识别码)、domain、version、remarklicense_key、status、expire_at、apply_id
auth.query查授权详情code 或 licensestatus、expire_at、days_left、online
auth.check校验是否有效code 或 licensevalid、reason、days_left
auth.heartbeat心跳上报code、version、domainserver_time、status
auth.unbind解绑识别码code成功后状态回到 pending
auth.file取授权识别文件code 或 licensefilename、content(直接写盘即可)
app.info应用信息无platform_public_key、check_interval
app.notice公告列表无list、site_notice
app.config远程配置无config(键值对)
app.version检查更新version(当前版本)has_update、version、download_url、is_force
app.stats应用统计无total、active、online、today

返回统一是 {code, msg, data, timestamp, sign},code 为 0 表示成功,sign 是平台用系统私钥对 json(data) + "|" + timestamp 的签名,可以用 app.info 返回的 platform_public_key 验。

五、错误码

code含义code含义
0成功2001授权记录不存在
1001缺少参数2002授权未生效
1002签名校验失败2004识别码被拉黑
1003应用ID不存在4001调用太频繁
1004API密钥不对5001服务端异常
1005应用被禁用1008接口不存在
1006时间戳超范围1007nonce 重复

六、授权识别文件怎么用

1. 授权通过后,在后台下载文件,或者在程序里调 auth.file 拿内容。
2. 文件名是随机生成的,别改,直接上传到站点根目录。
3. 浏览器打开 http://你的域名/随机文件名.php,能看到授权号、到期时间、状态就说明部署对了。
4. 授权站每 300 秒访问一次这个文件做巡查,会带一个随机 challenge,文件用自己的私钥签名后回给授权站,改文件、删文件、过期都会被查出来。
5. 取消授权后,下一次巡查会给这个文件下发吊销指令,文件自己改成失效状态,不需要手工去删。
6. 程序里可以直接 include 这个文件做本地校验,断网也能判。

<?php
// 站点这边本地校验,把授权文件放根目录,程序启动时 include 一下
require_once __DIR__ . '/sdk/zy_client.php';

// 文件名是下发时的随机名,换成自己那个
$r = zy_local_check(__DIR__ . '/k8f3m1x0qz.php');

if (!$r['valid']) {
    exit('授权已失效:' . $r['status']);
}
echo '授权正常';
echo $r['days_left'] < 0 ? ',永久有效' : ',还剩 ' . $r['days_left'] . ' 天';

七、PHP SDK

sdk/zy_client.php 已经把签名和验签都包好了,直接用:

<?php
require_once __DIR__ . '/sdk/zy_client.php';

$appId  = 'zy1a2b3c4d5e6f7g';           // 应用ID
$apiKey = '你的API密钥';                 // API密钥
$priKey = file_get_contents(__DIR__ . '/app_private.pem'); // 应用私钥

$c = new ZyAuthClient($appId, $apiKey, $priKey, 'https://auth.demo.com');

// 1. 提交授权申请,code 是唯一识别码(机器码、域名、自定义标识都行)
$r = $c->apply('MAC-8C1645A9B2D1', 'https://demo.com', '1.0.0');
echo $r['status'];   // pending 等待授权 / active 已授权

// 2. 程序启动时校验
$r = $c->check('MAC-8C1645A9B2D1');
if ($r['valid']) {
    echo '授权正常,剩余天数:' . $r['days_left'];
} else {
    exit('授权无效:' . $r['reason']);
}

// 3. 拿识别文件写到站点根目录
$f = $c->getFile('MAC-8C1645A9B2D1');
file_put_contents(__DIR__ . '/' . $f['filename'], $f['content']);

八、Python 示例

import time, base64, json, urllib.parse, urllib.request
# 需要 pip install pycryptodome
from Crypto.Signature import pkcs1_15
from Crypto.Hash import SHA256
from Crypto.PublicKey import RSA

APP_ID = 'zy1a2b3c4d5e6f7g'
API_KEY = '你的API密钥'
PRI = open('app_private.pem').read()

def call(action, extra=None):
    p = {
        'action': action,
        'appid': APP_ID,
        'apikey': API_KEY,
        'timestamp': str(int(time.time())),
        'nonce': str(int(time.time() * 1000)),
    }
    if extra:
        p.update(extra)
    sign_str = '&'.join('%s=%s' % (k, p[k]) for k in sorted(p.keys()))
    key = RSA.import_key(PRI)
    sig = pkcs1_15.new(key).sign(SHA256.new(sign_str.encode()))
    p['sign'] = base64.b64encode(sig).decode()
    url = 'https://auth.demo.com/api.php?' + urllib.parse.urlencode(p)
    return json.loads(urllib.request.urlopen(url, timeout=15).read())

r = call('auth.apply', {'code': 'MAC-8C1645A9B2D1', 'domain': 'https://demo.com'})
print(r)

九、巡查机制

授权站只巡查"下发过文件"的授权,也就是已经下载过识别文件、并且填了部署域名的那些。巡查时会做三件事:确认文件还在、确认文件没被改(验签)、确认授权状态和平台一致。任何一项不对就记为离线,管理员在巡查监控里能看到具体原因。

巡查有两种触发方式:服务器加计划任务 */5 * * * * php /www/wwwroot/sx5.aurour.xyz/zyauth/cron.php,或者什么都不配,页面被访问时会顺手触发一次,间隔同样是 5 分钟。

十、几个常见问题

签名总是失败?先确认待签串里没有 sign 自己,再确认参数排序是升序,最后看私钥是不是这个应用的,重置过密钥就要换新的。

到期时间不选会怎样?不选就是永久有效,数据库里 expire_at 为空,days_left 返回 -1。

换服务器怎么办?调 auth.unbind 解绑旧的识别码,新机器重新 auth.apply,原来的授权号会作废并重新生成。

API 调不动?后台的 API 日志里有每一次请求的结果码,先去那里看是哪一步卡住的。

ZY授权中心  v1.0.0  ·  对接文档