YOS-Pass 是由 广东思丞技术支持服务中心 研发的新一代统一身份认证平台。 它基于 OAuth 2.0 思想,采用二维码扫码 + JWT Token 签名验证机制,为 Web、App、小程序等多端应用提供安全、快捷的登录服务。
通过 YOS-Pass,你的用户无需记住账号密码,只需使用 YOS 万象 IOA App 扫描二维码,即可一键完成登录, 同时你的应用无需存储用户密码,大幅降低安全风险。
已接入 支持管理员 支持第三方应用 签名防篡改
| 计费项 | 单价 | 计费时机 | 说明 |
|---|---|---|---|
| 扫码登录成功 | 2 元/次 | Token 签发时 | 用户扫码确认授权后,YOS-Pass 生成 access_token 时自动扣费 |
| 发起登录请求 | 免费 | — | 调用 start_login 创建二维码不收费 |
| 轮询状态 | 免费 | — | 调用 session_status 轮询不收费 |
complete_login() 生成 JWT Token 时扣费,扫码后用户取消不扣费。used_quota >= total_quota 时,新登录请求返回错误码 3002(可用余额不足),需联系管理员充值。3001(速率限制)。第三方应用接入 YOS-Pass 只需以下 5 个步骤:
app_key 和 app_secret。
app_key 和 app_secret 生成签名,调用 api/start_login.php,获取二维码数据。
api/session_status.php,授权成功后获得 access_token(JWT 格式)。
access_token 进行 JWT 签名验证,通过后完成登录。
推荐 如果你的 App 或网站不想自行实现轮询,可以使用我们提供的 通用登录页(login.php),只需跳转链接即可完成全流程。
请求头
Content-Type: application/json
X-Request-Signature: {HMAC-SHA256}
请求体
{
"app_key": "your_app_key",
"app_secret": "your_app_secret",
"timestamp": 1234567890,
"nonce": "random_string"
}
返回(成功)
{
"success": true,
"error_code": 0,
"session_id": "64位hex",
"qr_data": "base64编码的JSON",
"expires_in": 150
}
返回(失败)
{
"success": false,
"error_code": 4004,
"message": "域名不在白名单内"
}
返回(待授权)
{
"status": "pending",
"success": true,
"message": "等待扫码授权"
}
返回(已授权)
{
"status": "authorized",
"success": true,
"access_token": "jwt_token"
}
返回(已过期)
{
"status": "expired",
"error_code": 1002,
"message": "二维码已过期"
}
提示 建议轮询间隔 2 秒,超时 150 秒。
请求体
{
"session_id": "xxx",
"signature": "HMAC签名",
"timestamp": 1234567890
}
返回
{
"success": true,
"error_code": 0,
"message": "授权成功",
"access_token": "token",
"type": "admin" 或 "app"
}
此接口同时支持管理员二维码和第三方二维码,自动识别类型。签名算法:HMAC-SHA256(session_id|timestamp, app_secret)。
整个接入流程涉及 两类密钥,各自用途不同,请勿混淆:
| 密钥名称 | 存储位置 | 用途 |
|---|---|---|
app_secret每 App 独立 |
YOS-Pass DB also in your config.php |
① start_login 请求签名(youquan → YOS-Pass) ② authorize 扫码签名(YOS IOA App → YOS-Pass) ③ JWT Token 签发(YOS-Pass complete_login) |
YOS_APP_SECRET每 App 独立 |
your config.php | ④ JWT Token 验签(your app 服务端本地) 值等于你的 app_secret |
关键 ③ JWT 签发和④ JWT 验签使用同一把密钥(你自己的 app_secret),而非系统全局密钥。 每个 App 的密钥独立,互不干扰——你的 app_secret 只签你自己的 Token。
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ youquan │ │ YOS-Pass │ │ YOS IOA App │
│ (第三方App) │ │ (认证服务) │ │ (扫码端) │
└──────┬───────┘ └──────┬───────┘ └──────┬───────┘
│ │ │
│ ① POST start_login │ │
│ 签名: YOS_APP_SECRET │ │
│ Body: {app_key, ts, nonce} │ │
├──────────────────────────────►│ │
│ │ │
│ ◄── {session_id, qr_data} │ │
│ │ │
│ ② 展示二维码 │ │
│ │ ③ POST authorize │
│ │ 签名: app_secret │
│ │ Body: {session_id, sig, ts} │
│ │◄──────────────────────────────┤
│ │ │
│ │ DB: status=1 ────────────────►│
│ │ │
│ ④ GET session_status │ │
│ ?session_id=xxx │ │
├──────────────────────────────►│ │
│ │ complete_login() │
│ │ JWT 签名: app_secret │
│ │ │
│ ◄── {access_token: JWT} │ │
│ │ │
│ ⑤ 本地 verifyJwt() │ │
│ 密钥: YOS_APP_SECRET │ │
│ → 验签通过,登录成功 │ │
| 步骤 | 请求方 | 目标 | 签名/验签密钥 |
|---|---|---|---|
| ① | youquan 服务端 | YOS-Pass start_login | YOS_APP_SECRET(每App独立) |
| ② | youquan 前端 | 用户浏览器 | 无需密钥(纯展示) |
| ③ | YOS IOA App | YOS-Pass authorize | app_secret(从DB读取) |
| ④ | youquan 前端 | YOS-Pass session_status | 无需签名(GET请求) |
| ⑤ | youquan 服务端 | 本地 JWT 验签 | YOS_APP_SECRET(即你的 app_secret) |
扫码授权成功后返回的 access_token 是一个 JWT(JSON Web Token),
你的服务端必须对该 Token 进行签名验证后才能信任其中的用户身份,
否则相当于未做任何鉴权。以下是完整规范:
Token 由三段组成,以 "." 分隔:header.payload.signature
// Header(固定) { "alg": "HS256", "typ": "JWT" } // Payload 示例 { "user_id": 1, "exp": 1783810118, "iat": 1783802918 }
| 字段 | 说明 |
|---|---|
alg | 签名算法,固定为 HS256(HMAC-SHA256) |
typ | Token 类型,固定为 JWT |
user_id | 用户唯一标识 |
iat | Token 签发时间(Unix 时间戳) |
exp | Token 过期时间(Unix 时间戳),默认有效期为签发后 2 小时(7200 秒) |
重要 JWT 签名密钥是你自己的 app_secret。
YOS-Pass 在 complete_login() 中会从数据库查出你注册的 app_secret 来签发 Token。
你需要在服务端配置 YOS_APP_SECRET 为与 app_secret 相同的值来验证 Token。
不同 App 的密钥独立,天然隔离,一个 App 的 Token 无法被另一个 App 用来验签。
注意 YOS-Pass 的 JWT 使用 标准 Base64 编码
(PHP 的 base64_encode / base64_decode),
不是 JWT 规范中的 Base64URL 编码。
这意味着编码结果中可能包含 +、/、= 字符。
." 分割 Token,得到三部分:Header、Payload、Signatureexp 是否已过期(建议额外加上 ±90 秒的时钟偏差容忍)Header.Payload"(拼接时使用原始 Base64 编码值,不含 "." 且不做二次解码)做 HMAC-SHA256 签名user_id 完成登录/** * 验证 YOS-Pass JWT Token * @param string $token 完整的 JWT Token 字符串 * @param string $secret YOS-Pass 统一签名密钥(从管理员处获取) * @return array|false 验签成功返回 payload 数组,失败返回 false */ function verifyYosToken($token, $secret) { $parts = explode('.', $token); if (count($parts) !== 3) { return false; // Token 格式错误 } list($headerB64, $payloadB64, $signatureB64) = $parts; // 使用标准 base64_decode(非 base64url) $header = json_decode(base64_decode($headerB64), true); $payload = json_decode(base64_decode($payloadB64), true); $signature = base64_decode($signatureB64); if (!$header || !$payload || !$signature) { return false; // 解码失败 } // 校验算法 if ($header['alg'] !== 'HS256') { return false; } // 校验过期时间(±90 秒时钟偏差容忍) if (isset($payload['exp']) && time() - 90 > $payload['exp']) { return false; // Token 已过期 } // 计算签名:签名原文为 "headerB64.payloadB64"(原始 Base64 字符串,不做二次编码) $expectedSig = hash_hmac('sha256', $headerB64 . '.' . $payloadB64, $secret, true); // 二进制安全比对 if (!hash_equals($expectedSig, $signature)) { return false; // 签名不匹配 } return $payload; } // 使用示例: // $payload = verifyYosToken($_POST['access_token'], $jwtSecret); // if ($payload) { // $_SESSION['user_id'] = $payload['user_id']; // } else { // die('Token 验证失败'); // }
app_secret 签发,验签时必须用同一把密钥(即 YOS_APP_SECRET = app_secret)。base64_encode),不是 JWT 规范的 Base64URL(-/_)。
所有 POST 请求(除授权接口外)必须在请求头 X-Request-Signature 中携带 HMAC-SHA256 签名,
签名原文为 整个请求体(raw body),密钥为 app_secret,签名结果经 Base64 编码。
// PHP 服务端签名示例
$secret = 'your_app_secret';
$body = file_get_contents('php://input');
$signature = base64_encode(hash_hmac('sha256', $body, $secret, true));
// JavaScript(浏览器)签名示例(仅用于测试)
const signature = CryptoJS.HmacSHA256(body, appSecret).toString(CryptoJS.enc.Base64);
所有接口返回的错误码定义如下,便于您快速定位问题。
| 错误码 | 常量 | 说明 |
|---|---|---|
| 0 | ERR_SUCCESS | 成功 |
| 1001 | ERR_QR_GENERATE_FAIL | 二维码生成失败 |
| 1002 | ERR_QR_EXPIRED | 二维码已过期 |
| 1003 | ERR_QR_INVALID_SESSION | 无效会话 |
| 2001 | ERR_APP_NOT_FOUND | 应用不存在 |
| 2002 | ERR_APP_DISABLED | 应用已禁用 |
| 3001 | ERR_RATE_LIMIT | 速率限制 |
| 3002 | ERR_QUOTA_EXCEEDED | 可用余额不足 |
| 4001 | ERR_MISSING_PARAM | 缺少必要参数 |
| 4002 | ERR_INVALID_SIGNATURE | 签名无效 |
| 4003 | ERR_TIMESTAMP_INVALID | 时间戳无效或已过期 |
| 4004 | ERR_DOMAIN_NOT_ALLOWED | 域名不在白名单内 |
| 4005 | ERR_SESSION_USED | 会话已使用 |
| 4006 | ERR_SESSION_NOT_FOUND | 会话不存在 |
| 4007 | ERR_MISSING_SESSION_ID | 缺少 session_id |
| 4008 | ERR_MISSING_SIGNATURE | 缺少签名或时间戳 |
| 4009 | ERR_REQUEST_EXPIRED | 请求已过期 |
| 5000 | ERR_SERVER_ERROR | 服务器内部错误 |
| 5001 | ERR_TOKEN_GENERATE_FAIL | 令牌生成失败 |
// ===== 配置 ===== $jwtSecret = 'YOS-Pass 统一签名密钥'; // 从管理员处获取 $appKey = 'your_app_key'; $appSecret = 'your_app_secret'; // ===== 1. 发起登录,获取二维码 ===== $apiUrl = 'https://auth.ycceo.com/YOS-Pass/api/start_login.php'; $data = [ 'app_key' => $appKey, 'app_secret' => $appSecret, 'timestamp' => time(), 'nonce' => bin2hex(random_bytes(8)) ]; $body = json_encode($data); $signature = base64_encode(hash_hmac('sha256', $body, $appSecret, true)); $ch = curl_init($apiUrl); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $body); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Content-Type: application/json', 'X-Request-Signature: ' . $signature ]); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $response = curl_exec($ch); $result = json_decode($response, true); if ($result['success']) { $sessionId = $result['session_id']; $qrData = base64_decode($result['qr_data']); // 用于生成二维码图片 // 将 $qrData 或 $sessionId 传给前端 } // ===== 2. 前端轮询(建议每 2 秒一次,超时 150 秒)===== // GET https://auth.ycceo.com/YOS-Pass/api/session_status.php?session_id=xxx // 当 status === 'authorized' 时,将 access_token 用 POST 提交给你的服务端 // ===== 3. 服务端验证 Token(你的 login callback)===== // 收到前端传来的 access_token 后: $token = $_POST['access_token']; $payload = verifyYosToken($token, $jwtSecret); // verifyYosToken 见上文第 4 节 if ($payload) { $_SESSION['user_id'] = $payload['user_id']; // 登录成功,跳转或返回 } else { http_response_code(401); die(json_encode(['error' => 'Token 无效或已过期'])); }
app_secret 必须存储在服务端,禁止暴露给前端或客户端。access_token 后必须在服务端验签,不能仅凭 Token 存在就信任登录。timestamp 与当前时间差值,建议 ±90 秒,防止重放攻击。exp 字段,拒绝使用已过期的 Token。app_secret 和 JWT 签名密钥(如每季度),降低泄露风险。如有接入问题或定制需求,请联系我们的技术支持团队: