简介:这是一套基于彩虹聚合登录系统二次开发的登录聚合管理后台,面向需要为多个站点快速接入第三方快捷登录的开发者、运维人员,旨在把QQ、微信、支付宝、微博、百度等平台登录能力统一收敛到中转API,以减少重复申请与维护量。整套资源共312个文件,以PHP、JavaScript、CSS、JSON、TypeScript为主要类型,另含SQL数据库脚本、说明文档、开发文档和SDK,压缩包仅4.08MB;后台采用光年layuiadmin改版,界面更精致,同时新增前台界面与站点配置,可直接修改前台展示内容。已有751人学习或下载。系统提供多应用管理、域名限制、账号记录、登录记录等能力,并支持为每个应用独立配置参数与回调地址;目前QQ中转登录链路已跑通,附QQ互联申请注意事项、Discuz论坛预申请说明及回调地址return.php配置方法,能帮助读者快速理解聚合登录接入流程;借助包内SDK与文档,还可继续扩展微信、支付宝等平台,适合网站开发者用于快速搭建统一快捷登录模块。
1. 彩虹登录聚合系统网站到底在做什么:多个平台一次接完
用户访问一个新站点,如果登录区只有手机号注册一种入口,这一步就会流失一部分人。挂上「QQ 登录」「微信登录」「GitHub 登录」按钮,注册成本就变成一次点击。「彩虹登录聚合系统网站」做的就是把这堆第三方登录收进同一套服务:统一跳转、统一回调、统一把平台用户映射成本地用户,最后用一份配置管所有平台。它的目标用户是正在做网站或 App、想提供多种登录方式又不愿为每个平台各写一套 OAuth 对接代码的开发者。下面按我实际搭建这套系统的路径来写:先讲 OAuth 授权码原理,再落适配器与用户表设计,然后是部署和避坑,最后补两个进阶玩法。
2. OAuth 2.0 授权码模式:聚合登录的地基
2.1 授权码流程的五个步骤拆解
第三方登录的底层都是 OAuth 2.0 的授权码模式(Authorization Code),从发起跳转到用户回到你的站点,固定五步:
- 用户在站点点击「QQ 登录」,站点把浏览器重定向到 QQ 授权页,URL 携带 client_id、redirect_uri、response_type=code、state。
- 用户在授权页完成自己的账号授权。
- 平台把浏览器重定向到配置好的 redirect_uri,并在 query 上附带 code 和 state。
- 服务端拿 code 到平台后端接口换取 access_token。这一步必须放在服务端。
- 服务端用 access_token 调「获取用户信息」接口,拿到 open_id、昵称、头像等信息。
这五步的通用性很强,难度集中在第 5 步的平台差异上。QQ 返回的昵称是 nickname、头像在 figureurl_qq_2;微信返回 nickname、headimgurl,若你同时在多个应用里接微信,还要区分 openid 与 unionid;GitHub 返回 login、avatar_url,email 只有用户显式授权才会给。返回格式也各不一样:GitHub 强制 JSON,但需要带好认证头;QQ 的 token 接口返回的是access_token=xxx&expires_in=7776000这种 query 风格;QQ 的 openid 接口干脆是 JSONP 包裹的callback({...})。所以聚合层存在的第一个理由,就是把各平台五花八门的返参统一成一套内部用户模型。
2.2 各平台授权差异与选型要点
做选择的时候可以把这张表打印出来贴显示器边:
| 项目 | QQ 互联 | 微信开放平台 | 微博 | GitHub |
|---|---|---|---|---|
| 授权入口 | graph.qq.com/oauth2.0/authorize | open.weixin.qq.com/connect/qrconnect | api.weibo.com/oauth2/authorize | github.com/login/oauth/authorize |
| code 换 token | GET | GET | POST | POST |
| token 返回 | 非 JSON | JSON | JSON | 默认非 JSON |
| 唯一标识 | openid | openid / unionid | id | id |
| 昵称字段 | nickname | nickname | screen_name | login |
| 头像字段 | figureurl_qq_2 | headimgurl | avatar_hd | avatar_url |
| 手机号/邮箱 | 默认不给 | 默认不给 | 需另申请 | 需用户授权 |
先说微信的 unionid。openid 是应用级唯一标识,你在微信开放平台建了站点 A 和站点 B 两个应用,同一用户两边的 openid 不同;unionid 是同一主体下多个应用共用的标识,只有做了开放平台账号绑定后才返回。如果你后续要打通网站、公众号、小程序三端的用户身份,就必须把 unionid 一并存下来。这也是我在用户表里单独留了 union_id 字段的原因。
再说手机号。QQ 和微信基本不向普通开发者开放手机号接口,微博需要单独申请且审核严格,GitHub 只有用户主动勾选 email 授权才会返回。这意味着聚合登录替代不了「手机号验证」这一环,凡是业务强依赖手机号通知、风控的,都得在用户绑定页上自己补一个手机号填写与验证流程。等到做流量投放或黑产防控时,这个设计能不能撑住,实际上是当初定用户表时决定的。
2.3 state 参数:回调安全底线
接入第三方登录时有一个参数如果忽略,系统基本等于没穿外套。state 是发起跳转前由服务端生成的随机字符串,随授权链接带到平台,平台回调时原样带回来,服务端比对一致才继续。没有它,攻击者可以诱导用户点一个恶意构造的授权链接,平台回调后系统若不做校验,攻击者就能把受害者的平台账号绑到自己名下。state 实现上就两行:
// 跳转前生成 state,并存入当前会话 $_SESSION['oauth_state_qq'] = bin2hex(random_bytes(16)); // 回调时取出比对 if (($_GET['state'] ?? '') !== ($_SESSION['oauth_state_qq'] ?? '')) { exit('state 校验失败,疑似 CSRF'); }注意我给 state 的 key 加了前缀_qq,这个是后来补上的。原本我只用一个oauth_state,线上出现过多标签页互相覆盖导致登录失败的问题,改成按 provider 隔离后才解决。这里提前写上,别再踩。
3. 设计聚合层:适配器模式与用户身份统一
3.1 统一入口:一个 URL 拉起所有平台跳转
聚合登录的入口层可以分成前端按钮、跳转控制器、回调控制器三层。前端什么逻辑都不用有,按钮直接指向统一入口:
<?php // login.php:统一跳转控制器 require_once 'config.php'; require_once 'lib/OAuthAdapter.php'; require_once 'lib/QQProvider.php'; require_once 'lib/WechatProvider.php'; require_once 'lib/GitHubProvider.php'; $provider = $_GET['provider'] ?? ''; $action = $_GET['action'] ?? 'redirect'; $providers = [ 'qq' => new QQProvider($config['providers']['qq']), 'wechat' => new WechatProvider($config['providers']['wechat']), 'weibo' => new WeiboProvider($config['providers']['weibo']), 'github' => new GitHubProvider($config['providers']['github']), ]; if (!isset($providers[$provider])) { http_response_code(404); exit('不支持的登录方式'); } $adapter = $providers[$provider]; if ($action === 'redirect') { header('Location: ' . $adapter->buildAuthorizeUrl()); exit; } if ($action === 'callback') { require __DIR__ . '/callback.php'; }所有平台走同一个入口。新接入平台只做两件事:往 providers 数组里加一个适配器实例,在 config 里补一组 app_id、app_secret、redirect_uri。入口文件不再改变。回调逻辑拆到独立文件是因为它要做的事比跳转多得多——换 token、拉信息、查绑定、写会话,硬塞在这个文件里会越来越臃肿,后续管理员连按钮都看不懂。
3.2 适配器基类与平台实现差异
适配器是这个聚合层的核心抽象。每个平台一个类,继承同一个基类,三个方法:buildAuthorizeUrl、getAccessToken、getUserInfo。基类里统一管理凭证和回调地址:
<?php // lib/OAuthAdapter.php abstract class OAuthAdapter { protected string $appId; protected string $appSecret; protected string $redirectUri; protected string $state; public function __construct(array $conf) { $this->appId = $conf['app_id']; $this->appSecret = $conf['app_secret']; $this->redirectUri = $conf['redirect_uri']; $this->state = bin2hex(random_bytes(16)); } abstract public function buildAuthorizeUrl(): string; abstract public function getAccessToken(string $code): string; abstract public function getUserInfo(string $accessToken): array; }有一个容易掉进去的坑值得先说:state 虽然在适配器里生成,但不能保存在适配器实例里,因为 PHP 的每个 HTTP 请求都是全新的进程、全新的对象。跳转请求里生成的 state,到回调请求时对象已经重建了。state 必须在跳转时写进 session,回调时从 session 取。
GitHub 适配器是最适合第一个写的,流程简单、接口干净:
<?php // lib/GitHubProvider.php class GitHubProvider extends OAuthAdapter { public function buildAuthorizeUrl(): string { return 'https://github.com/login/oauth/authorize?' . http_build_query([ 'client_id' => $this->appId, 'redirect_uri' => $this->redirectUri, 'scope' => 'read:user user:email', 'state' => $this->state, ]); } public function getAccessToken(string $code): string { $ch = curl_init('https://github.com/login/oauth/access_token'); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_HTTPHEADER, ['Accept: application/json']); curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([ 'client_id' => $this->appId, 'client_secret' => $this->appSecret, 'code' => $code, 'redirect_uri' => $this->redirectUri, ])); $resp = curl_exec($ch); curl_close($ch); $data = json_decode($resp, true); return $data['access_token'] ?? ''; } public function getUserInfo(string $accessToken): array { $ch = curl_init('https://api.github.com/user'); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Authorization: token ' . $accessToken, 'User-Agent: RainbowLogin/1.0', ]); $resp = curl_exec($ch); curl_close($ch); $data = json_decode($resp, true); return [ 'open_id' => (string) $data['id'], 'nickname' => $data['login'], 'avatar' => $data['avatar_url'], 'email' => $data['email'] ?? '', ]; } }GitHub 有两个细节容易第一次就翻车:token 接口默认返回access_token=xxx这种 query 文本而不是 JSON,必须带Accept: application/json才给 JSON;user 接口不带User-Agent会直接 403。第三方平台接口测试时,先用 curl 在命令行里把响应原样打出来看结构,能省大量时间。
QQ 适配器就明显不一样:token 接口返回access_token=xxx&expires_in=...,openid 接口返回 JSONP:
// QQ 获取 openid 的解析片段 $resp = file_get_contents( 'https://graph.qq.com/oauth2.0/me?access_token=' . urlencode($token) ); // 返回 callback( {"client_id":"...","openid":"..."} ); $json = substr($resp, strlen('callback('), -2); $openid = json_decode($json, true)['openid'] ?? '';所以不是所有平台都规规矩矩返回 JSON,适配器实现里要单独处理这种情况。这也是我不太建议直接套用通用 OAuth 库的原因之一——通用库默认平台返回 JSON,遇到 QQ 这种野路子响应不是不能配,而是配起来并不比手写一个适配器简单多少,一旦平台升级改了返回格式,中间层反而是最晚被发现的。
3.3 用户表设计:本地账号与第三方绑定分离
这套聚合系统的地基是用户表,设计错了后面补起来非常痛苦。我在几个项目里用过两版设计,最终稳定在「user + oauth_bind 两张表」的结构:
CREATE TABLE `user` ( `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, `username` VARCHAR(50) NOT NULL DEFAULT '', `phone` VARCHAR(20) NOT NULL DEFAULT '', `email` VARCHAR(100) NOT NULL DEFAULT '', `avatar` VARCHAR(255) NOT NULL DEFAULT '', `status` TINYINT NOT NULL DEFAULT 1, `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_phone` (`phone`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE `oauth_bind` ( `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, `user_id` INT UNSIGNED NOT NULL, `provider` VARCHAR(20) NOT NULL COMMENT '平台标识:qq/wechat/weibo/github', `open_id` VARCHAR(64) NOT NULL COMMENT '平台侧唯一标识', `union_id` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '微信 unionid,其他平台留空', `extra` TEXT NOT NULL COMMENT '平台原始资料 JSON', `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_provider_openid` (`provider`, `open_id`), KEY `idx_user_id` (`user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;不建议直接在 user 表里加 provider、open_id 列。原因很简单:一个用户会同时绑定 QQ、微信、GitHub 多个账号,列式设计要么只能存一个,要么得加一堆冗余列。拆表后 user_id 与 oauth_bind 是一对多关系,天然支持多平台绑定。
uk_provider_openid唯一索引是防重复绑定的最后一道物理防线。即使业务代码漏了判断,数据库层面也会拒绝同平台同 open_id 插第二行。extra字段存平台返回的原始资料 JSON,上线初期排查用户资料映射问题时特别好用,别省。
3.4 回调处理:从授权码到登录态
callback.php 是聚合系统的关键路径,四步处理:校验 state、换 token、拉用户信息、查绑定。
<?php // callback.php $provider = $_GET['provider'] ?? ''; $code = $_GET['code'] ?? ''; $state = $_GET['state'] ?? ''; if ($state !== ($_SESSION["oauth_state_{$provider}"] ?? '')) { exit('state 校验失败'); } $adapter = $providers[$provider]; $accessToken = $adapter->getAccessToken($code); $platformUser = $adapter->getUserInfo($accessToken); $bind = findBind($provider, $platformUser['open_id']); if ($bind) { $_SESSION['user_id'] = $bind['user_id']; header('Location: /user.php'); exit; } // 未绑定,暂存跳转绑定页 $_SESSION['pending_provider'] = $provider; $_SESSION['pending_user'] = $platformUser; header('Location: /bind.php');第 4 步「未绑定」分支需要特别注意:不要在这里直接建新用户。因为你拿到的只是一个平台账号,用户很可能已经在本地注册过,只是没绑过当前平台。直接建号会让同一用户产生多个本地账号,后期合并成本很高。绑定页应该给「新用户注册」和「已有账号绑定」两条路,已有账号那条靠手机号验证码或账号密码完成。
4. 部署与配置:把聚合登录跑通在真实服务器上
4.1 申请各平台应用凭证与回调地址
代码动手前先把四个平台的应用创建一遍。QQ 互联和微信开放平台都需要审核,一般要提交网站备案号和网站 Logo,审核期从几小时到两三天不等。微博创建应用也需要审核,GitHub 不需要审核,个人开发者注册后在 Settings 里的 Developer settings 直接建 OAuth App 就能拿到 client_id 和 client_secret。如果你是第一次接,建议先拿 GitHub 打通全流程,因为反馈快、文档最规范,然后再接 QQ/微信。
常见做法是准备一个专门的子域名,比如 auth.example.com,所有平台的回调地址都用这个域名,避免以后业务域名变动导致所有回调地址要重新在平台后台改一遍。
<?php // config.php return [ 'session' => [ 'name' => 'RAINBOW_LOGIN_SESSION', ], 'providers' => [ 'qq' => [ 'app_id' => '101234501', 'app_secret' => '替换成你的密钥', 'redirect_uri' => 'https://auth.example.com/callback.php?provider=qq', ], 'wechat' => [ 'app_id' => 'wx1234567890abcdef', 'app_secret' => '替换成你的密钥', 'redirect_uri' => 'https://auth.example.com/callback.php?provider=wechat', ], 'github' => [ 'app_id' => 'Ov23liabcdef123456', 'app_secret' => '替换成你的密钥', 'redirect_uri' => 'https://auth.example.com/callback.php?provider=github', ], ], ];app_secret 千万不要提交进 Git 仓库。单独写在部署机上的环境变量或私有文件里,PHP 通过 getenv() 读取,是底线操作。config.php 至少要有「不在 web 可访问目录下」的保障,Nginx 会直接拒绝访问点文件的话这点能挡住一半事故。
关于回调地址有个容易踩的细节:部分平台后台只允许填域名,回调 URL 的其余部分由平台拼接;部分平台要求填完整 URL 且校验严格,像 GitHub 连 query 参数都会比对。所以 config 里和平台后台必须保持字节级一致。如果平台拒绝带 query 的回调地址,把 provider 放在路径上,配合 Nginx 重写到 callback.php,下面部署部分会给配置。
4.2 初始化数据库
建表语句已在上一章给出,保存为 schema.sql 后执行:
mysql -u root -p auth_db < schema.sql建完检查唯一索引:
SHOW INDEX FROM oauth_bind;重点确认 Key_name 为 uk_provider_openid 的 Non_unique 为 0。没建上索引的话,后面并发绑定很可能会出现重复数据,而这类数据在业务层极难察觉,往往要到用户投诉「我的账号被顶掉」才发现。
4.3 Nginx + PHP-FPM 部署要点
生产环境建议把聚合登录单独部署成一个站点,不要和其他业务代码混目录。Nginx 配置要点是:回调路径重写、php-fpm 转发、HTTPS 强制。
server { listen 443 ssl; server_name auth.example.com; ssl_certificate /etc/nginx/ssl/auth.example.com.pem; ssl_certificate_key /etc/nginx/ssl/auth.example.com.key; root /var/www/rainbow-login; index index.php; location / { try_files $uri $uri/ /index.php?$query_string; } # /callback/qq 这类路径重写到 callback.php?provider=qq location ~ ^/callback/(qq|wechat|weibo|github)$ { rewrite ^/callback/(.*)$ /callback.php?provider=$1 break; fastcgi_pass 127.0.0.1:9000; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root/callback.php; } location ~ \.php$ { fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; } }这样平台后台填的回调地址就可以写成https://auth.example.com/callback/qq,而代码内部仍然走统一回调入口。路径这种方式的好处是后台的 URL 不带问号,很多平台的校验更宽松。
强制 HTTPS:QQ 和微信开放平台都要求回调地址必须是 HTTPS,HTTP 的回调在授权后会直接报错。所以要给这个站点提前备好证书,部署顺序最好是证书就绪后直接配 HTTPS,不要先上 HTTP 再改。
4.4 用 curl 快速冒烟跳转链路
部署完成后先别急着浏览器点按钮,用 curl 把跳转链路跑一遍:
curl -I "https://auth.example.com/login.php?provider=github&action=redirect"预期输出HTTP/1.1 302和 Location 指向 GitHub 授权页,URL 里能看到 client_id、redirect_uri、state 三个参数。这一步能验证配置加载、state 生成、跳转 URL 拼接、Nginx 是否放行。
再验证会话:
curl -v -c cookies.txt "https://auth.example.com/login.php?provider=github&action=redirect" 2>&1 | grep 'Set-Cookie'能看到 Set-Cookie 里带上了会话 ID 和过期时间。想看服务端 state 是否写入 session,可以临时写一个 debug 接口输出$_SESSION,生产上线前删掉。
真正的 code 换 token 只能通过真实浏览器授权完成,curl 帮不上忙。GitHub 的 code 是一次性的、时效很短,平台也不会允许你在命令行里伪造。所以完整链路的验证方式就是浏览器点一遍:点击登录 → 授权 → 跳回 → 看到绑定页或用户中心。
5. 避坑指南:聚合登录最容易翻车的五个地方
5.1 state 校验失败:多标签页覆盖是根源
现象:线上反馈用户点击登录后平台正常回调,服务端却一直报 state 校验失败。
原因:用户同时开了两个标签页,分别点了同一个平台的登录,两次跳转各生成一个新 state,但 session 里只存一份,后写入的覆盖了前面的。回调回来的是第一个页面的旧 state,比对不通过。
解决:按 provider 隔离存储,同一平台允许存一份最新的即可;更稳妥的做法是存一个 state 数组,回调时用 in_array 判断。前面在 2.3 里写成带 provider 前缀的 session key,就是这个场景的血泪经验。
5.2 手机号/邮箱字段为空,业务被卡死
现象:回调信息里昵称、头像都有,但 email、phone 永远为空字符串,业务方却拿手机号去做唯一性校验。
原因:平台根本不给。QQ互联手机号接口需要独立申请且门槛不低;微信不提供;微博需要额外权限;GitHub 只有用户在授权页勾选 email 授权才返回,且用户名就是 login 而不是昵称。
解决:第三方登录不要承载强依赖手机号的风控逻辑。绑定页增加手机号补充表单,短信验证码通过后写入 user 表。user 表里 phone 有唯一索引,绑定手机号时如果已存在,引导用户去登录原账号再完成合并。
5.3 回调地址不一致,授权页直接报错
现象:跳转到平台授权页后立即出现 redirect_uri 不匹配的错误。
原因:三处不一致——平台后台配置的回调、代码 config 里的 redirect_uri、浏览器实际落地的回调 URL。常见是开发时换过测试域名、忘了 https、加了端口、或者带 query 参数被平台拒绝。
解决:对齐三处,字节级一致。GitHub 严格比对整条 URL 包括 query;QQ互联按域名匹配。若平台不接受带 query 的回调,改用 /callback/qq 路径方式配合 Nginx 重写。
5.4 一人多号:同一用户在不同平台产生两个账号
现象:用户先用 QQ 登录创建了账号 A,之后用微信登录,系统又新建了账号 B。用户数据被拆成两套,投诉不断。
原因:回调处理简单粗暴——查不到 oauth_bind 就直接 insert user。没有把「登录方式」和「用户身份」分开理解。平台账号只是一个凭证,一个用户可以有多个凭证。
解决:查不到绑定关系时,不要直接建号。把平台用户信息暂存到 session,跳转绑定页,让用户选择新注册还是绑定已有账号。绑定已有账号时用手机号+短信验证码或账号密码校验,匹配到 user_id 后写入 oauth_bind。
5.5 本地开发联调效率太低
现象:本地起服务想把 OAuth 调试通,但平台回调过不来,只能反复改配置。
原因:OAuth 平台要求的回调地址基本都是公网可达的 URL,localhost、127.0.0.1 一般填不了,填了也过不了校验。
解决:我习惯把测试环境直接放到一台有公网域名的云服务器上,本地只写代码和单元测试,回调验证统一在测试环境做。等代码稳定再把域名切换成正式域名。本地反复折腾各种回调映射方案,最后上线还要改配置,纯属浪费精力。
6. 进阶:JWT 会话管理与多平台绑定更新
6.1 登录成功后签发 JWT 而不是 Session
聚合登录做完后,前端形态大概率不只有网页,还会有小程序或 App。小程序里没有 cookie,传统 session 的传递方式就不太适用。JWT 是更顺手的方案:登录成功后服务端签发一个带过期时间的 token,客户端存本地,后续请求带着 Authorization 头。自己实现最小签发并不复杂:
function base64url_encode(string $data): string { return rtrim(strtr(base64_encode($data), '+/', '-_'), '='); } function create_jwt(array $payload, string $secret): string { $header = ['alg' => 'HS256', 'typ' => 'JWT']; $payload = $payload + ['iat' => time(), 'exp' => time() + 7200]; $segments = [ base64url_encode(json_encode($header, JSON_UNESCAPED_UNICODE)), base64url_encode(json_encode($payload, JSON_UNESCAPED_UNICODE)), ]; $signature = hash_hmac('sha256', implode('.', $segments), $secret, true); $segments[] = base64url_encode($signature); return implode('.', $segments); }签发时把 user_id、provider 放进 payload,exp 设 2 小时。生产上建议把有效期压短,配合 refresh_token 续期,比单靠一个长 token 稳。
6.2 同一本地账号绑定多个平台
oauth_bind 表支持一个 user_id 对应多个平台。已登录用户点「绑定微信」,回调后拿到 user_id 插入一行;未登录用户首次用平台账号登录,走绑定页「已有账号绑定」分流。插入前先查 open_id 是否已属于其他 user_id,是就提示「该平台账号已绑定其他账号」,依赖 uk_provider_openid 兜底。
6.3 冒烟验证清单
上线前按五步跑:未登录用 GitHub 登录应进绑定页;手机号绑新账号成功;退出再用同一 GitHub 登录应直接进用户中心;用户中心绑第二个平台后退出,用第二平台登录应进同一账号;换未绑定过的新平台账号应回绑定页且不覆盖存量绑定。我第一次做这类系统漏了第 4 条,上线后收到「同一 QQ 绑了两个账号」的投诉,根源是绑定页漏查归属。把「绑定前先查归属」固化为逻辑后,这个问题再没出现过。聚合登录的协议细节交给平台文档,用户身份的合并与绑定才是决定质量的地方。希望帮到你。
本文还有配套的精品资源,点击获取