零度聚合 是一款社会化账号聚合登录系统,基于 OAuth2.0 协议,为网站提供统一、便捷的第三方登录服务。 开发者只需简单的几步操作,即可在自己的网站上集成 QQ、微信、支付宝、微博、GitHub 等多种第三方登录方式。
聚合登录的完整流程如下图所示:
目前共支持 25 种第三方登录平台,以下是各平台对应的 type 参数值:
| type 值 | 平台名称 | type 值 | 平台名称 |
|---|---|---|---|
| wx | 微信 | ||
| alipay | 支付宝 | sina | 微博 |
| baidu | 百度 | github | GitHub |
| gitee | Gitee | ||
| microsoft | Microsoft | dingtalk | 钉钉 |
| feishu | 飞书 | douyin | 抖音 |
| huawei | 华为 | xiaomi | 小米 |
| douban | 豆瓣 | gitlab | GitLab |
| gitea | Gitea | gitcode | GitCode |
| jihulab | 极狐GitLab | openatom | OpenAtom |
| wework | 企业微信 | oschina | OSChina |
| aliyun | 阿里云 |
各平台是否可用取决于管理员是否在后台开启并配置了对应的密钥。微信(wx)和支付宝(alipay)额外支持扫码登录模式。
只需 4 步即可完成第三方登录接入:
访问用户中心注册账号,进入应用管理创建您的应用,获取 AppID 和 AppKey。
在应用设置中填写您网站的回调地址(如 https://yourdomain.com/callback.php),并添加授权域名。
在您的网站登录页面,通过 API 获取登录跳转地址,引导用户跳转至第三方授权页面。
用户授权后,系统会回调至您设置的地址,您通过回调接口即可直接获取用户信息。
以下是一个简单的 PHP 接入示例:
<?php
// 配置信息
$appid = '您的AppID';
$appkey = '您的AppKey';
$callback = 'https://yourdomain.com/callback.php';
// 发起登录 - 以QQ为例
$loginUrl = "https://您的域名/connect.php?act=login"
. "&appid={$appid}"
. "&appkey={$appkey}"
. "&type=qq"
. "&redirect_uri=" . urlencode($callback);
// 请求API获取跳转地址
$result = json_decode(file_get_contents($loginUrl), true);
if ($result['code'] == 0) {
// 跳转到第三方授权页面
header('Location: ' . $result['url']);
exit;
} else {
echo '登录发起失败:' . $result['msg'];
}
?>
<?php
// callback.php - 处理登录回调
$appid = '您的AppID';
$appkey = '您的AppKey';
if (isset($_GET['type']) && isset($_GET['code'])) {
$type = $_GET['type'];
$code = $_GET['code'];
// 通过回调接口直接获取用户信息
$apiUrl = "https://您的域名/connect.php"
. "?act=callback"
. "&appid={$appid}"
. "&appkey={$appkey}"
. "&type={$type}"
. "&code={$code}";
$response = file_get_contents($apiUrl);
$data = json_decode($response, true);
if ($data['code'] == 2) {
// 用户尚未完成登录,需要等待重试
echo '登录尚未完成,请稍后...';
} elseif ($data['code'] == 0) {
// 登录成功,获取用户信息
echo '用户昵称:' . $data['nickname'] . '<br>';
echo '用户头像:' . $data['faceimg'] . '<br>';
echo '唯一标识:' . $data['social_uid'] . '<br>';
// 请使用 social_uid 作为用户唯一标识进行绑定
} else {
echo '登录失败:' . $data['msg'];
}
}
?>
实际生产环境中,建议在收到 code=2 时进行轮询重试,直到获取到最终结果:
<?php
// callback.php - 生产环境推荐写法
$appid = '您的AppID';
$appkey = '您的AppKey';
$apiBase = 'https://您的域名';
if (!isset($_GET['type']) || !isset($_GET['code'])) {
die('缺少必要参数');
}
$type = $_GET['type'];
$code = $_GET['code'];
$maxRetry = 10; // 最大重试次数
$interval = 2000000; // 重试间隔 2 秒(微秒)
for ($i = 0; $i < $maxRetry; $i++) {
$apiUrl = "{$apiBase}/connect.php?act=callback"
. "&appid={$appid}&appkey={$appkey}"
. "&type={$type}&code={$code}";
$response = file_get_contents($apiUrl);
$data = json_decode($response, true);
if ($data['code'] == 0) {
// 登录成功
// 使用 social_uid 查找或创建本地用户
$user = findOrCreateUser($data['type'], $data['social_uid']);
$user->nickname = $data['nickname'];
$user->avatar = $data['faceimg'];
$user->save();
// 设置登录 Session
$_SESSION['user_id'] = $user->id;
header('Location: /dashboard.php');
exit;
} elseif ($data['code'] == 2) {
// 用户尚未完成授权,等待后重试
usleep($interval);
continue;
} else {
// 登录失败
die('登录失败[' . $data['errcode'] . ']:' . $data['msg']);
}
}
die('登录超时,请重试');
?>
对于 Vue/React 等前后端分离项目,后端只需提供两个代理接口即可:
const express = require('express');
const axios = require('axios');
const app = express();
const APPID = '您的AppID';
const APPKEY = '您的AppKey';
const API_BASE = 'https://您的域名';
// 1. 前端调用此接口获取登录跳转URL
app.get('/api/login', async (req, res) => {
const { type, redirect_uri } = req.query;
const url = `${API_BASE}/connect.php?act=login`
+ `&appid=${APPID}&appkey=${APPKEY}`
+ `&type=${type}&redirect_uri=${encodeURIComponent(redirect_uri)}`;
const { data } = await axios.get(url);
if (data.code === 0) {
res.json({ success: true, url: data.url });
} else {
res.json({ success: false, msg: data.msg });
}
});
// 2. 前端回调后调用此接口获取用户信息
app.get('/api/callback', async (req, res) => {
const { type, code } = req.query;
const url = `${API_BASE}/connect.php?act=callback`
+ `&appid=${APPID}&appkey=${APPKEY}`
+ `&type=${type}&code=${code}`;
const { data } = await axios.get(url);
if (data.code === 0) {
// data.social_uid / data.nickname / data.faceimg
res.json({ success: true, user: data });
} else if (data.code === 2) {
res.json({ success: false, pending: true, msg: '登录中...' });
} else {
res.json({ success: false, msg: data.msg });
}
});
回调地址(Redirect URI)是用户完成第三方平台授权后,系统将用户重定向回您网站的地址。该地址会携带授权码 code 参数。
http:// 或 https:// 开头登录成功后,系统会重定向到您设置的回调地址,并附加以下参数:
| 参数 | 类型 | 必返回 | 说明 |
|---|---|---|---|
| type | String | 是 | 登录类型,如 qq、wx、alipay 等 |
| code | String | 是 | 授权码,用于通过回调接口获取用户信息 |
| state | String | 否 | 自定义状态参数,原样返回,用于防止CSRF攻击 |
回调示例:https://yourdomain.com/callback.php?type=qq&code=520DD95263C1CFEA0870&state=your_state
通过此接口获取第三方平台的登录跳转地址,引导用户前往授权。
/connect.php?act=login
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| act | String | 是 | 固定值:login |
| appid | String | 是 | 应用的 AppID |
| appkey | String | 是 | 应用的 AppKey(请勿在前端暴露) |
| type | String | 是 | 登录类型:qq、wx、alipay、sina、github 等 |
| redirect_uri | String | 是 | 授权回调地址,需 URL 编码 |
| state | String | 否 | 自定义状态参数,回调时原样返回,建议传递以防止 CSRF 攻击 |
{
"code": 0,
"msg": "succ",
"type": "qq",
"url": "https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=xxx&redirect_uri=xxx&state=xxx"
}
微信(wx)和支付宝(alipay)登录时,还会额外返回 qrcode 字段,可用于生成扫码登录二维码。
{
"code": -1,
"errcode": 103,
"msg": "回调域名未授权"
}
用户完成第三方授权后,使用回调收到的 code 直接换取用户信息。无需分两步获取 Token,一步即可拿到完整的用户数据。
/connect.php?act=callback
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| act | String | 是 | 固定值:callback |
| appid | String | 是 | 应用的 AppID |
| appkey | String | 是 | 应用的 AppKey |
| type | String | 是 | 登录类型,与发起登录时一致 |
| code | String | 是 | 回调时收到的授权码 |
{
"code": 0,
"msg": "succ",
"type": "qq",
"access_token": "89DC9691E274D6B596FFCB8D43368234",
"social_uid": "AD3F5033279C8187CBCBB29235D5F827",
"faceimg": "https://thirdqq.qlogo.cn/g?b=oidb&k=...",
"nickname": "用户昵称",
"location": "北京市",
"gender": "男",
"ip": "1.12.3.40"
}
重要:返回 code: 2 表示用户尚未完成第三方登录授权,建议间隔 2 秒后重试。
{
"code": 2,
"msg": "登录中"
}
{
"code": -1,
"errcode": 102,
"msg": "记录不存在"
}
| 参数 | 类型 | 说明 |
|---|---|---|
| code | Int | 状态码:0=成功,2=未完成登录,-1=失败 |
| msg | String | 状态信息 |
| type | String | 登录方式 |
| social_uid | String | 第三方用户唯一标识(请以此字段绑定用户) |
| access_token | String | 第三方登录 Token |
| nickname | String | 用户昵称 |
| faceimg | String | 用户头像 URL |
| gender | String | 用户性别(男/女) |
| location | String | 用户所在地(部分平台返回) |
| ip | String | 用户登录 IP |
在用户登录后的任意时间,可以使用此接口再次查询该用户的详细信息。
/connect.php?act=query
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| act | String | 是 | 固定值:query |
| appid | String | 是 | 应用的 AppID |
| appkey | String | 是 | 应用的 AppKey |
| type | String | 是 | 登录方式 |
| social_uid | String | 是 | 第三方用户唯一标识 |
{
"code": 0,
"msg": "succ",
"type": "qq",
"social_uid": "AD3F5033279C8187CBCBB29235D5F827",
"access_token": "89DC9691E274D6B596FFCB8D43368234",
"nickname": "用户昵称",
"faceimg": "https://thirdqq.qlogo.cn/g?...",
"location": "北京市",
"gender": "男",
"ip": "1.12.3.40"
}
{
"code": -1,
"msg": "none"
}
| 参数 | 类型 | 说明 |
|---|---|---|
| code | Int | 状态码:0=成功,-1=用户不存在 |
| msg | String | succ=成功,none=用户不存在 |
| social_uid | String | 第三方用户唯一标识 |
| access_token | String | 第三方登录 Token |
| nickname | String | 用户昵称 |
| faceimg | String | 用户头像 URL |
| gender | String | 用户性别(男/女) |
| location | String | 用户所在地(部分平台返回) |
| ip | String | 用户登录 IP |
所有接口在请求失败时,统一返回 {"code": -1, "errcode": xxx, "msg": "错误描述"} 格式。
| code | errcode | 说明 | 解决方案 |
|---|---|---|---|
| 0 | - | 请求成功 | - |
| 2 | - | 用户尚未完成第三方登录 | 间隔 2 秒后重新请求回调接口 |
| -1 | 101 | 缺少必要参数 | 检查 act、appid、appkey 等必填参数是否传递 |
| -1 | 102 | 应用不存在 / 应用已关闭 / 应用审核中 / 应用审核未通过 / 授权记录不存在或已失效 | 检查 AppID 是否正确,确认应用状态正常,或重新发起登录 |
| -1 | 103 | AppKey 不正确 / 回调域名未授权 / 该登录方式无法使用 / 用户被封禁 | 检查 AppKey 是否正确;在应用设置中添加回调授权域名 |
| -1 | 104 | 登录方式未开启或未配置 / 未知登录方式 | 确认该登录方式已启用并正确配置了密钥 |
| -1 | 201 | 数据库错误 | 联系管理员排查 |
| -1 | 301 | 第三方平台返回错误 | 查看 msg 中的具体错误信息,检查第三方平台密钥配置 |
生产环境中,推荐使用 cURL 发起 HTTP 请求,它支持超时控制、SSL 验证等关键特性:
<?php
function httpGet($url, $timeout = 10) {
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => $timeout,
CURLOPT_CONNECTTIMEOUT => $timeout,
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_FOLLOWLOCATION => true,
]);
$response = curl_exec($ch);
$errno = curl_errno($ch);
curl_close($ch);
if ($errno) {
return ['error' => true, 'msg' => curl_error($ch)];
}
return json_decode($response, true);
}
// 使用示例
$result = httpGet("https://您的域名/connect.php?act=login&appid={$appid}&appkey={$appkey}&type=qq&redirect_uri=" . urlencode($callback));
if ($result['code'] == 0) {
header('Location: ' . $result['url']);
exit;
}
?>
收到 code=2 时,推荐使用递增间隔的轮询策略,避免频繁请求:
<?php
$maxRetry = 15;
$intervals = [2, 2, 2, 3, 3, 3, 5, 5, 5, 5, 5, 8, 8, 8, 10]; // 递增间隔(秒)
for ($i = 0; $i < $maxRetry; $i++) {
$data = httpGet($apiUrl);
if ($data['code'] == 0) {
// 登录成功,处理用户信息
break;
} elseif ($data['code'] == 2) {
sleep($intervals[$i]);
continue;
} else {
die('登录失败: ' . $data['msg']);
}
}
die('登录超时');
?>
推荐使用 type + social_uid 的组合键作为用户唯一标识,建表建议:
CREATE TABLE `user_oauth` ( `id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, `user_id` INT UNSIGNED NOT NULL, `type` VARCHAR(20) NOT NULL COMMENT '登录方式: qq/wx/alipay等', `social_uid` VARCHAR(64) NOT NULL COMMENT '第三方用户唯一标识', `nickname` VARCHAR(100) DEFAULT NULL, `avatar` VARCHAR(500) DEFAULT NULL, `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY `uk_type_social` (`type`, `social_uid`), KEY `idx_user_id` (`user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
code 字段,不要只判断是否为 0errcode 和 msg 到日志系统,便于排查问题code=2 设置最大重试次数,避免无限轮询social_uid 不能为空qrcode 字段生成二维码,配合轮询完成登录注册并登录用户中心,在「应用管理」中创建应用即可获取。
请在用户中心的应用设置中添加您网站的域名到授权域名列表。例如您的回调地址是 https://www.example.com/callback.php,则需要添加域名 www.example.com。
目前支持 QQ、微信、支付宝、微博、百度、GitHub、Google、Facebook、Gitee、Twitter、Microsoft、钉钉、飞书、抖音、华为、小米、豆瓣、GitLab、Gitea、GitCode、极狐GitLab、OpenAtom、企业微信、OSChina、阿里云等 25 种平台。
code=2 表示用户在第三方平台的授权尚未完成。建议在收到此返回后,间隔 2 秒重新请求回调接口,直到返回 code=0(成功)或 code=-1(失败)。
支持。所有接口均为标准的 HTTP GET API,返回 JSON 格式数据,任何能发送 HTTP 请求的语言和框架都可以使用。
登录失败时接口会返回 {"code": -1, "errcode": xxx, "msg": "错误描述"},请根据 errcode 和 msg 进行排查处理。常见原因包括:AppID/AppKey 错误、回调域名未授权、登录方式未配置等。
微信和支付宝支持扫码登录。发起登录接口成功时,除了返回跳转 URL 外,还会额外返回 qrcode 字段。您可以将此 URL 生成二维码展示给用户扫码登录,适用于 PC 端场景。
不能。用户可能修改昵称或更换头像,请务必使用 social_uid 作为用户唯一标识进行账号绑定。
支持。您可以根据自己的需求自定义登录页面样式和流程,只需按照 API 文档的要求发起登录请求并处理回调即可。
授权码自生成后 10 分钟内有效。超过此时间后再次使用 code 请求回调接口,将返回 {"code":-1,"errcode":102,"msg":"记录不存在"},需要引导用户重新发起登录。
不会。social_uid 是由第三方平台的用户 ID 经加密处理后的固定值,只要同一个第三方账号,无论何时登录,social_uid 始终相同。
发起登录接口(act=login)在 type=wx 时会额外返回 qrcode 字段。您可以将此 URL 使用 QRCode 库生成二维码图片展示给用户扫码,扫码成功后用户页面会自动跳转到回调地址。
如果您的回调地址本身已包含 type 参数,系统会自动检测并避免重复添加。其他参数则正常追加在回调地址后面。
系统没有硬性的频率限制,但建议合理控制轮询频率(建议间隔 2 秒以上),避免对服务端造成过大压力。同时请设置最大重试次数(推荐 10-15 次)。
第三方平台的 access_token 有效期由各平台自行决定,通常在 30 天到永久不等。聚合登录系统会尽可能保持 token 的有效性,但如果用户在第三方平台取消了授权,token 可能会失效。建议每次用户登录时更新本地存储的 token。
在用户首次通过第三方登录成功后,使用 social_uid 在您的数据库中创建或匹配本地用户账号,然后生成您自己的 Session 或 JWT Token 返回给前端。之后前端每次请求时携带该 Token 即可,无需重复走第三方授权流程。
系统返回的 social_uid 仅在同平台内唯一。如需跨平台关联,需要您自行实现账号绑定逻辑。推荐方案:首次登录时自动创建新账号,后续提供"账号合并"功能让用户手动绑定。
file_get_contents 默认没有超时控制,在生产环境中不建议使用。推荐改用 cURL 并设置 CURLOPT_TIMEOUT 参数(建议 10 秒)。具体写法请参考上方「最佳实践」章节的 cURL 封装函数。