授权代码
把授权校验接进你的应用:三步接入,含机器指纹上报的完整代码
接入流程
首次接入用卡密激活一次;之后每次启动只调用校验接口
1
首次激活
用卡密调用 auth_apply.php,一次性绑定域名 + 来源 IP + 机器指纹2
嵌入校验
启动或定时调用 auth_check.php,机器指纹随请求一起上报3
按结果放行
authorized 为 true 才允许使用,否则提示并退出接口一览
全部为 POST,Content-Type 用 application/x-www-form-urlencoded
| 接口 | 用途 | 调用时机 |
|---|---|---|
| /api/auth_apply.php | 用卡密激活授权,绑定域名、来源 IP、机器指纹 | 仅首次,一次 |
| /api/auth_check.php | 校验授权,顺带上报机器指纹与出口 IP | 每次启动 / 定时 |
| /api/auth_upgrade.php | 校验「版本升级」独立授权(只校验域名) | 用到升级功能时 |
| /api/pirate_report.php | 上报未授权使用的线索(可选) | 可选 |
1 · 首次激活
POST /api/auth_apply.php — 只在第一次接入时调用
| 参数 | 必填 | 说明 |
|---|---|---|
| auth_code | 是 | 管理员发给你的卡密,形如 EYE-XXXX-XXXX-XXXX-XXXX,只能用一次 |
| domain_or_ip | 是 | 你的域名,例如 www.example.com;一个域名只能授权一次 |
| allowed_ip | 是 | 你服务器的出口 IP(不是访客 IP)。多个用逗号分隔,支持 1.2.3.* 与 1.2.3.0/24 |
| machine_id | 否 | 机器指纹。这里带上就立刻绑定;不带则等首次校验时自动绑定 |
POST https://sqkm.sgkami.cn/api/auth_apply.php
auth_code=EYE-1234-5678-9ABC-DEF0
domain_or_ip=www.example.com
allowed_ip=1.2.3.4
machine_id=2f8a1c...
// 返回
{ "status": "success", "message": "授权成功..." }
{ "status": "error", "message": "授权失败:授权码无效或已被使用" }
2 · 授权校验
POST /api/auth_check.php — 每次启动都调用,机器指纹从这里上报
| 参数 | 必填 | 说明 |
|---|---|---|
| domain_or_ip | 是 | 你的域名,例如 $_SERVER['SERVER_NAME'] |
| machine_id | 建议 | 机器指纹。注意:授权已绑定机器指纹后,这里传空值或换了机器都会判为未授权 |
POST https://sqkm.sgkami.cn/api/auth_check.php
domain_or_ip=www.example.com
machine_id=2f8a1c...
// 返回
{ "status": "success", "message": "授权验证通过", "authorized": true }
{ "status": "error", "message": "未授权或授权已过期", "authorized": false }
三要素怎么分工
域名和机器指纹由客户端上报;来源 IP 由授权服务器自动获取(取 TCP 层的真实连接 IP,无需也不应该在参数里传)。
每次校验都会刷新后台的「最近上报 IP / 机器指纹」,机器指纹为空时会自动绑定(首次信任)。
还没有授权码?
先把校验代码部署上去、访问一次你的站点,管理员的「待授权设备」里就会出现你的域名、出口 IP 和机器指纹,
然后管理员一键建档即可,无需修改代码。
3 · 机器指纹怎么取
直接复制这个函数,Linux / Windows 都能用
function get_machine_id() {
// 1) Linux(systemd / dbus)
foreach (array('/etc/machine-id', '/var/lib/dbus/machine-id') as $p) {
if (@is_readable($p)) {
$v = trim(@file_get_contents($p));
if ($v !== '') { return strtolower(preg_replace('/[^0-9A-Za-z]/', '', $v)); }
}
}
// 2) Windows:主板 UUID
if (stripos(PHP_OS, 'WIN') === 0) {
// wmic 在新版 Windows 已移除,取不到时退回 PowerShell
$cmds = array(
'wmic csproduct get uuid 2>NUL',
'powershell -NoProfile -Command "(Get-CimInstance Win32_ComputerSystemProduct).UUID" 2>NUL',
);
foreach ($cmds as $cmd) {
$v = @shell_exec($cmd);
if (!$v) { continue; }
$v = strtolower(preg_replace('/[^0-9A-Za-z]/', '', str_ireplace('UUID', '', $v)));
if (strlen($v) >= 8) { return $v; }
}
}
// 3) 兜底:换主机名 / 重装系统后会变,需要在后台「解绑」后重新绑定
return strtolower(md5(php_uname('n') . '|' . php_uname('m')));
}
读不到机器指纹时不要传空值
@file_get_contents() 失败会返回空字符串。如果授权已经绑定了机器指纹,
传空值会被判为「机器不匹配」而整站显示未授权。请确保 get_machine_id() 一定有返回值(上面第 3 条的兜底逻辑就是为此)。
4 · 完整 PHP 示例
复制即可用:激活 + 校验 + 超时保护 + 机器指纹
<?php // ================= 授权系统接入 ================= // 授权服务器地址写死,不要用 $_SERVER['HTTP_HOST'](那是你自己的域名) define('LICENSE_API', 'https://sqkm.sgkami.cn/api'); // 你的卡密,激活成功后可以删掉这一行 define('LICENSE_CARD', 'EYE-XXXX-XXXX-XXXX-XXXX'); function get_machine_id() { foreach (array('/etc/machine-id', '/var/lib/dbus/machine-id') as $p) { if (@is_readable($p)) { $v = trim(@file_get_contents($p)); if ($v !== '') { return strtolower(preg_replace('/[^0-9A-Za-z]/', '', $v)); } } } if (stripos(PHP_OS, 'WIN') === 0) { // wmic 在新版 Windows 已移除,取不到时退回 PowerShell $cmds = array( 'wmic csproduct get uuid 2>NUL', 'powershell -NoProfile -Command "(Get-CimInstance Win32_ComputerSystemProduct).UUID" 2>NUL', ); foreach ($cmds as $cmd) { $v = @shell_exec($cmd); if (!$v) { continue; } $v = strtolower(preg_replace('/[^0-9A-Za-z]/', '', str_ireplace('UUID', '', $v))); if (strlen($v) >= 8) { return $v; } } } return strtolower(md5(php_uname('n') . '|' . php_uname('m'))); } function license_post($path, $data) { $ch = curl_init(LICENSE_API . $path); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data)); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_TIMEOUT, 5); // 必须设超时,否则授权服务器故障会拖死你的站 curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 3); $raw = curl_exec($ch); curl_close($ch); $json = json_decode((string) $raw, true); return is_array($json) ? $json : array('authorized' => false, 'message' => '授权服务器无响应'); } // 首次激活(只调用一次,成功后可以删掉这段) function license_activate($code, $domain, $allowed_ip, $machine_id) { return license_post('/auth_apply.php', array( 'auth_code' => $code, 'domain_or_ip' => $domain, 'allowed_ip' => $allowed_ip, 'machine_id' => $machine_id, )); } // 授权校验 function license_check($domain, $machine_id) { return license_post('/auth_check.php', array( 'domain_or_ip' => $domain, 'machine_id' => $machine_id, )); } // ================= 使用 ================= $domain = $_SERVER['SERVER_NAME']; $machine_id = get_machine_id(); // 第一次部署时解除下面两行注释,激活一次即可(把出口 IP 换成你服务器的) // $r = license_activate(LICENSE_CARD, $domain, '1.2.3.4', $machine_id); // if ($r['status'] !== 'success') { echo '激活失败:' . $r['message']; exit; } $r = license_check($domain, $machine_id); if (empty($r['authorized'])) { echo '未授权使用!请联系管理员获取授权。'; exit; }
5 · Python 示例
import hashlib, platform, re, socket, subprocess, sys
import requests
LICENSE_API = 'https://sqkm.sgkami.cn/api'
DOMAIN = 'www.example.com'
LICENSE_CARD = 'EYE-XXXX-XXXX-XXXX-XXXX'
def get_machine_id() -> str:
for p in ('/etc/machine-id', '/var/lib/dbus/machine-id'):
try:
with open(p) as f:
v = f.read().strip()
if v:
return re.sub(r'[^0-9A-Za-z]', '', v).lower()
except OSError:
pass
if sys.platform.startswith('win'):
cmds = [['wmic', 'csproduct', 'get', 'uuid'],
['powershell', '-NoProfile', '-Command',
'(Get-CimInstance Win32_ComputerSystemProduct).UUID']]
for cmd in cmds: # wmic 在新版 Windows 已移除
try:
out = subprocess.check_output(cmd, stderr=subprocess.DEVNULL, text=True)
v = re.sub(r'[^0-9A-Za-z]', '', out.replace('UUID', '')).lower()
if len(v) >= 8:
return v
except Exception:
continue
return hashlib.md5((socket.gethostname() + '|' + platform.machine()).encode()).hexdigest()
def license_post(path: str, data: dict) -> dict:
r = requests.post(f'{LICENSE_API}{path}', data=data, timeout=5)
return r.json()
def license_check(machine_id: str) -> dict:
return license_post('/auth_check.php',
{'domain_or_ip': DOMAIN, 'machine_id': machine_id})
# 首次激活(只跑一次)
# print(license_post('/auth_apply.php', {'auth_code': LICENSE_CARD, 'domain_or_ip': DOMAIN,
# 'allowed_ip': '1.2.3.4', 'machine_id': get_machine_id()}))
if not license_check(get_machine_id()).get('authorized'):
print('未授权使用!请联系管理员获取授权。')
raise SystemExit(1)
6 · 常见坑
| 坑 | 后果 | 正确做法 |
|---|---|---|
| 接口地址用 $_SERVER['HTTP_HOST'] 拼 | 请求打到你自己站上,永远返回未授权 | 写死授权服务器地址 |
| curl 不设超时 | 授权服务器抖动时你的站跟着挂 | 设 CURLOPT_TIMEOUT = 5 |
| 机器指纹读不到时传空 | 已绑定的授权整站显示未授权 | 用上面的兜底逻辑保证有值 |
| allowed_ip 填了访客 IP | 校验时来源 IP 不匹配,判定未授权 | 填服务器出口 IP,多台用逗号分隔 |
| 中途改了机器指纹的取值方式 | 指纹变了,校验直接失败 | 先在后台「解绑」,再让客户站重新上报 |
| 同一域名授权两条记录 | 第二条会被拒绝创建 | 一个域名一条授权,先删旧的再加新的 |
| 升级功能单独授权 | auth_upgrade.php 只校验域名,不看机器指纹 | 升级授权是否开通、到期在后台单独控制 |