第三方对接指南
5 步完成 UserMatrix OAuth 2.1 + PKCE 对接,附可运行完整 demo。
统一接口标准:云集生态的官方标准 = OAuth 2.1 + OIDC(覆盖网站/APP/小程序,家族产品互联唯一固定协议)。本文是标准对接的实操指南。完整对比见 对接方式总览。
流程一览
你的应用 UserMatrix 用户
│ │ │
│── 1. 构造授权 URL ────────→│ │
│ (含 PKCE challenge) │ │
│ │── 2. 跳转授权页 ───────→│
│ │←── 3. 用户扫码/授权 ────│
│←── 4. 带 code 回调 ────────│ │
│── 5. code + verifier 换 token →│ │
│←── 返回 access_token ──────│ │
│ │ │
└─ 登录完成,用 access_token 调 userinfo
准备材料
在 um.yunjii.cn/console/apps 创建应用,获得:
| 材料 | 说明 | 示例 |
|---|---|---|
appid | 应用 ID(即 OAuth client_id) | 1001 |
appkey | 应用密钥(即 client_secret,切勿泄露) | a3f8b2c9d1e7... |
| 回调域名 | 你的回调地址域名 | yourapp.com |
5 步对接(OAuth 2.1 + PKCE)
构造 OAuth 2.1 授权 URL,引导用户跳转到 UM 授权页。公开客户端(SPA/小程序)必须启用 PKCE。
手动构造:
GET https://um.yunjii.cn/oauth/authorize
| 参数 | 必填 | 说明 |
|---|---|---|
response_type | ✅ | 固定 code |
client_id | ✅ | 应用 ID |
redirect_uri | ✅ | 回调地址(域名需在控制台配置的 domains 中) |
state | ✅ | 防 CSRF,随机字符串,原样返回 |
scope | ⬜ | openid profile(默认) |
code_challenge | ⬜ | PKCE challenge(推荐启用) |
code_challenge_method | ⬜ | S256 |
PHP SDK 一行生成:
$state = bin2hex(random_bytes(16));
$verifier = $um->generateCodeVerifier(); // PKCE
$authUrl = $um->authorizeUrl($state, $verifier); // 自动算 challenge
header("Location: $authUrl");
JS SDK:
const verifier = UM.generateCodeVerifier();
const challenge = await UM.generateCodeChallenge(verifier);
sessionStorage.setItem('um_pkce', verifier);
location.href = UM.authorizeUrl(state, challenge);
PKCE 流程:客户端生成 code_verifier(随机串)→ 计算 code_challenge = base64url(SHA256(verifier)) → 授权请求带 challenge → 换 token 时带 verifier → 服务端校验匹配。防止授权码被中间人劫持。
用户在 UM 授权页完成扫码(微信/支付宝)或登录(邮箱/密码)。
这一步你不需要做任何事,UM 会自动处理。
用户授权后,UM 会带 code 和 state 跳转回你的 redirect_uri:
https://yourapp.com/callback?code=CODE_xxx&state=你传的state
你需要:
- 验证
state与第一步生成的是否一致(防 CSRF) - 取出
code,进入下一步
code 5 分钟内只能用一次,过期或重复消费返回 errcode=40163。
接口:POST https://um.yunjii.cn/oauth/token
参数:
| 参数 | 必填 | 说明 |
|---|---|---|
grant_type | ✅ | authorization_code |
code | ✅ | 上一步拿到的 code |
client_id | ✅ | 应用 ID |
client_secret | ✅ | 应用密钥(appkey) |
redirect_uri | ✅ | 必须与授权时一致 |
code_verifier | ⬜ | PKCE verifier(启用 PKCE 时必传) |
响应:
{
"code": 0,
"msg": "succ",
"access_token": "eyJhbGciOiJIUzI1NiIs...",
"token_type": "Bearer",
"expires_in": 604800,
"refresh_token": "rt_a1b2c3d4e5f6...",
"user": {
"id": 12345,
"openid": "um_a1b2c3d4e5f6",
"nickname": "张三",
"avatar": "https://thirdwx.qlogo.cn/...",
"login_type": "wx"
}
}
字段说明:
| 字段 | 说明 |
|---|---|
access_token | 访问令牌(7 天有效),后续 API 调用凭证 |
refresh_token | 刷新令牌(30 天,轮换制) |
user.openid | 应用内用户唯一 ID(同应用不变) |
user.id | 用户内部 ID |
user.login_type | 登录方式(wx / alipay / qq / douyin / email / self) |
OAuth 2.1 标准端点用 code: 0 表示成功(旧版 REST 用 code: 1)。迁移时注意判断条件。
接口:GET https://um.yunjii.cn/oauth/userinfo
Authorization: Bearer <access_token>
响应(标准 OIDC claims):
{
"sub": "um_a1b2c3d4e5f6",
"name": "张三",
"nickname": "张三",
"picture": "https://thirdwx.qlogo.cn/...",
"email": "zhangsan@example.com",
"email_verified": true,
"login_type": "wx",
"iss": "https://um.yunjii.cn"
}
| 字段 | 说明 |
|---|---|
sub | UMID,跨应用唯一(同一用户在不同应用 UMID 相同) |
name / nickname | 用户资料 |
picture | 头像 URL |
email | 邮箱(可能为空) |
login_type | 登录方式 |
Token 续期(Refresh Token 轮换)
access_token 过期后,用 refresh_token 换取新的 token:
接口:POST https://um.yunjii.cn/oauth/token
| 参数 | 必填 | 说明 |
|---|---|---|
grant_type | ✅ | refresh_token |
refresh_token | ✅ | 上一次获取的 refresh_token |
client_id | ✅ | 应用 ID |
client_secret | ✅ | 应用密钥 |
轮换制:每次刷新签发新的 refresh_token,旧的立即失效。客户端必须用最新的 refresh_token 进行下次刷新。
完整 PHP Demo
<?php
require_once 'UM.class.php';
session_start();
$um = new UM(
'你的_appid',
'你的_appkey',
'https://yourapp.com/callback.php',
'https://um.yunjii.cn/'
);
$act = $_GET['act'] ?? '';
if ($act === 'login') {
// 1. 生成授权 URL + PKCE
$state = bin2hex(random_bytes(16));
$verifier = $um->generateCodeVerifier();
$_SESSION['oauth_state'] = $state;
$_SESSION['oauth_verifier'] = $verifier;
header('Location: ' . $um->authorizeUrl($state, $verifier));
exit;
}
if (isset($_GET['code'])) {
// 3-4. 回调:校验 state + 换 token
if ($_GET['state'] !== $_SESSION['oauth_state']) {
die('State 校验失败');
}
$tokenRes = $um->token($_GET['code'], $_SESSION['oauth_verifier']);
if ($tokenRes['code'] !== 0) {
die('换取 token 失败:' . $tokenRes['msg']);
}
// 5. 获取用户信息
$user = $um->userinfo($tokenRes['access_token']);
// 清理 PKCE 临时数据
unset($_SESSION['oauth_state'], $_SESSION['oauth_verifier']);
// 存储登录态
$_SESSION['access_token'] = $tokenRes['access_token'];
$_SESSION['refresh_token'] = $tokenRes['refresh_token'];
$_SESSION['user'] = $user;
echo "登录成功!<br>";
echo "昵称:{$user['nickname']}<br>";
echo "UMID:{$user['sub']}<br>";
echo "头像:<img src='{$user['picture']}' width=64><br>";
}
扫码登录(二维码图片 · 兼容方式)
适合 PC 端不想跳转授权页的场景,直接 <img> 渲染二维码:
$qrUrl = $um->qrcodeUrl('wx', 200, 'state-xyz');
echo '<img src="' . htmlspecialchars($qrUrl) . '" width="200" height="200" alt="登录二维码">';
用户扫码后 UM 按 redirect_uri 回调 code,后续走上面的「5 步对接」第 3 步起。
用通用 OAuth 库接入(推荐)
UM 是标准 OAuth 2.1 + OIDC 服务,可直接用通用库接入,无需安装 UM SDK:
- Next.js / Auth.js:配置
provider: um({ issuer: 'https://um.yunjii.cn' }) - Python / authlib:
oauth.register('um', server_metadata_url='https://um.yunjii.cn/.well-known/openid-configuration', ...) - Spring Security:
ClientRegistration.withIssuer('https://um.yunjii.cn')...
通用库自动从 OIDC Discovery 读取所有端点配置。
常见问题
| 问题 | 原因 | 解决 |
|---|---|---|
errcode=102 | appid 不存在或应用未审核 | 检查 appid,确认应用状态为"运行中" |
errcode=103 | client_secret 不正确 | 检查 appkey |
errcode=104 | code 不存在/未授权/已过期 | code 5 分钟过期,重新发起授权 |
errcode=105 | timestamp 过期(旧版 REST) | 保证服务器时间正确,5 分钟内有效 |
errcode=107 | PKCE code_verifier 校验失败 | 检查 verifier 与 challenge 是否匹配 |
errcode=40163 | code 重复消费 | code 只能用一次,5 分钟过期 |