简介:本资源是专为Discuz!论坛管理员与PHP开发者设计的QQ快捷登录插件——北岸QQ登录改进版2.4,解决传统注册流程繁琐、用户流失率高、社交登录兼容性差等实际运营痛点,尤其适用于面向年轻用户的社区型站点。压缩包共61个文件,含24个核心PHP逻辑文件(如qqoauth.inc.php、httqqlogin.class.php、回调与绑定模块)、12张JPG/PNG图标与界面素材、8个HTM/HTML前端模板页,以及XML插件配置文件和安装卸载脚本,整体仅1MB,轻量易部署。目前已有429人学习下载,说明其在中小论坛定制化开发中具备较高实践认可度。用户可直接获取完整可运行的插件工程结构、多编码版本适配(GBK/UTF8/BIG5)、移动端支持类(mobilehttqqlogin.class.php)、详细API对接逻辑及腾讯开放平台IDKEY配置范例,无需从零开发即可快速集成稳定、安全、响应迅速的QQ一键登录能力。
1. DZ QQ登录插件北岸QQ登录 改进版2.4:不是“一键接入”,而是把Discuz! X3.5+环境下QQ第三方登录的认证链、会话同步和用户映射问题一次性理清楚
你刚在某高校论坛升级到Discuz! X3.5.2,发现原生QQ登录突然不返回unionid、头像字段为空、老用户重复注册、手机端点击登录直接白屏——这不是插件坏了,是QQ OpenAPI v3.0.0接口策略收紧后,旧版OAuth2.0流程与Discuz! 用户中心(uc_client)的session生命周期错位导致的典型症状。DZ QQ登录插件北岸QQ登录 改进版2.4 正是为解决这一类跨版本兼容性断裂而生:它不替换Discuz!核心鉴权逻辑,而是在source/plugin/qqlogin/目录下用最小侵入方式重写了授权回调处理、用户信息拉取、本地UID绑定与头像自动同步四层关键路径。适合正在维护Discuz! X3.4–X3.5系列社区、且已申请QQ互联应用但被“回调地址校验失败”“access_token过期不刷新”卡住的运维人员或二次开发工程师。它不是通用OAuth SDK,而是专为Discuz!用户表结构(pre_common_member)、UC通信协议(uc_user_synlogin)和QQ OpenAPI v3响应格式定制的缝合补丁。
2. 从零部署:用改进版2.4在Discuz! X3.5.2上跑通QQ登录全流程
2.1 环境确认与前置依赖检查
Discuz! X3.5.2默认不启用cURL扩展中的CURLOPT_SSL_VERIFYPEER严格校验,但QQ OpenAPI v3强制要求TLS 1.2+及完整证书链。若服务器PHP版本≥7.2.5,需手动确认以下三项:
# 检查cURL是否支持TLS 1.2+ php -r "echo curl_version()['features'] & CURL_VERSION_SSL ? 'SSL OK' : 'NO SSL';" # 检查OpenSSL版本(必须≥1.0.2u) openssl version # 检查Discuz! UC通信状态(后台→站长→UCenter设置→测试连接) # 若显示“UCenter 通信失败”,先修复UC通信,否则插件无法写入用户关系提示:很多翻车源于UC通信未通却强行启用插件。
source/uc_client/client.php中uc_user_login()调用失败时,插件不会报错,只会静默跳转回登录页——这是第一个必须掐死的黑匣子。
2.2 插件文件结构解析与关键路径定位
改进版2.4采用“覆盖式安装”,解压后得到标准Discuz!插件目录结构:
qqlogin/ ├── install.php # 插件安装脚本(仅执行一次,创建qqlogin_config表) ├── uninstall.php # 卸载脚本(清空配置,不删用户数据) ├── qqlogin.class.php # 核心类:含QQOAuth2Client、QQUserMapper、QQSessionSync三模块 ├── template/ # 登录按钮模板(支持PC/触屏双适配) │ ├── login.htm │ └── login_mobile.htm ├── include/ # 钩子注入点(关键!) │ └── qqlogin.inc.php # 被global.func.php调用,注入登录入口 └── admincp/ # 后台配置页(/admin.php?action=plugins&operation=config&identifier=qqlogin)重点盯住qqlogin.class.php中三个方法:
get_access_token_by_code():用code换token时,强制添加grant_type=authorization_code参数(原生Discuz!漏传,导致QQ返回invalid_grant)sync_user_info():拉取用户信息后,对figureurl_qq_2(高清头像)做HTTP 302跳转捕获,避免Discuz!默认保存302响应体导致头像URL失效bind_local_user():绑定时校验unionid而非openid,解决同一QQ号在多个应用间用户ID不一致问题
2.3 安装与后台配置实操步骤
- 将
qqlogin/文件夹上传至source/plugin/目录(注意路径:source/plugin/qqlogin/,非plugin/qqlogin/) - 后台进入「管理中心 → 扩展中心 → 插件管理」,找到「北岸QQ登录」,点击「安装」
- 安装成功后,进入「插件设置」页,填入QQ互联后台获取的三项:
- App ID(10位纯数字)
- App Key(32位小写字母+数字)
- 回调地址(必须与QQ互联后台填写的完全一致,含末尾斜杠,如
https://bbs.example.com/connect/qq/callback.php/)
注意:回调地址末尾斜杠是硬性要求。QQ OpenAPI v3校验时会对URL做
rtrim($url, '/') . '/'标准化,若Discuz!后台填/connect/qq/callback.php而QQ后台填/connect/qq/callback.php/,校验失败返回redirect_uri_mismatch。
- 关键一步:在「用户相关设置」中勾选「启用unionid绑定」并保存
- 此选项开启后,插件会在
pre_common_member_field_forum表中新增qq_unionid字段(通过install.php自动添加) - 若未勾选,插件退化为旧版逻辑,仍用
openid绑定,多应用间用户无法打通
- 此选项开启后,插件会在
3. 授权流程重写:为什么改进版2.4能绕过QQ OpenAPI v3的302跳转陷阱
3.1 QQ OpenAPI v3的头像URL机制变更
QQ OpenAPI v2返回的figureurl_qq_1/figureurl_qq_2是直连图片地址;v3改为返回302临时跳转URL,形如:https://q.qlogo.cn/qqapp/101XXXXXX/1234567890/100?term=3
该URL有效期仅5分钟,且每次请求返回不同跳转目标。Discuz!原生逻辑直接将此URL存入数据库,5分钟后头像全部失效。
改进版2.4的解决方案:在sync_user_info()中插入HTTP客户端主动跟随跳转,获取最终图片地址:
// qqlogin.class.php 第 287 行附近 public function fetch_avatar_url($qq_avatar_url) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $qq_avatar_url); curl_setopt($ch, CURLOPT_HEADER, false); curl_setopt($ch, CURLOPT_NOBODY, true); // 只取header curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // QQ证书链偶有异常,此处放宽 curl_setopt($ch, CURLOPT_TIMEOUT, 10); $result = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $finalUrl = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL); curl_close($ch); return $httpCode === 200 && filter_var($finalUrl, FILTER_VALIDATE_URL) ? $finalUrl : $qq_avatar_url; }逻辑说明:
CURLOPT_NOBODY=true+CURLOPT_HEADER=true组合,让cURL只发起HEAD请求,避免下载整张图浪费带宽CURLOPT_FOLLOWLOCATION=true强制跟随302,CURLINFO_EFFECTIVE_URL获取最终跳转地址- 失败时退回原始URL(兼容性兜底),但实际生产环境成功率>99.7%(某实验室压测数据)
3.2 access_token自动刷新机制设计
QQ access_token有效期2小时,refresh_token有效期30天。原生插件无刷新逻辑,用户登录2小时后再次访问需重新授权。改进版2.4在用户每次访问时检查token剩余时间:
// qqlogin.class.php 第 192 行 if ($this->token['expires_in'] - (TIMESTAMP - $this->token['create_time']) < 600) { // 剩余时间不足10分钟,立即刷新 $new_token = $this->refresh_access_token($this->token['refresh_token']); if (!empty($new_token['access_token'])) { $this->update_token($new_token); // 写入缓存并更新数据库 } }参数说明:
600:提前10分钟刷新,避免临界点失效update_token()方法将新token写入pre_common_pluginvar表(键名qqlogin_token),并更新create_time时间戳- 刷新失败时,插件不中断用户流程,继续使用旧token(因QQ允许少量超期请求)
4. 避坑指南:上线前必须验证的5个血泪现场
4.1 现象:点击QQ登录按钮后跳转到QQ授权页,授权完成后页面空白,URL停留在callback.php?code=xxx
原因:Discuz! X3.5.2的callback.php文件权限为644,但PHP-FPM运行用户(如www-data)无执行权限,导致PHP解析失败返回空内容
解决:chmod 755 source/plugin/qqlogin/callback.php,并确认Nginx/Apache配置中.php后缀由PHP处理器接管(非静态文件服务)
4.2 现象:后台配置页保存后提示“配置更新成功”,但前台登录按钮不显示
原因:Discuz!模板缓存未更新,template/default/common/header.htm中未加载插件钩子
解决:后台「界面 → 模板风格 → 默认风格 → 更新缓存」,或手动删除data/template/下所有*.tpl.php文件
4.3 现象:新用户首次登录后,个人中心显示“用户不存在”,但数据库pre_common_member中已存在该记录
原因:qqlogin.class.php中bind_local_user()方法调用discuz_user::add_user()时,未同步写入pre_ucenter_members表(UC用户表)
解决:检查install.php是否执行成功(查看pre_common_pluginvar表是否存在qqlogin_config记录),若缺失则手动执行install.php中的SQL建表语句
4.4 现象:手机端H5页面QQ登录后,返回Discuz!首页但未登录,F12看Network发现synlogin请求返回-1
原因:source/plugin/qqlogin/include/qqlogin.inc.php中qqlogin_synlogin()函数未适配移动端User-Agent,Discuz! X3.5.2的uc_user_synlogin()对移动端调用有额外session校验
解决:在qqlogin_synlogin()开头添加:
global $_G; $_G['mobile'] = 0; // 强制设为PC端,绕过移动端session校验4.5 现象:同一QQ号在A论坛登录后,切换到B论坛(同域名不同子目录)点击QQ登录,提示“该QQ号已被绑定”
原因:qq_unionid字段未设为UNIQUE索引,导致多站点共用一个Discuz!数据库时,unionid重复插入失败
解决:执行SQL手动添加唯一索引:
ALTER TABLE `pre_common_member_field_forum` ADD UNIQUE KEY `unionid` (`qq_unionid`);提示:此操作需停站执行,否则高并发下可能锁表。建议在凌晨低峰期操作。
5. 进阶验证:用三组真实请求日志反向验证插件行为是否符合预期
5.1 构建可复现的验证场景
准备三类测试账号:
- A账号:首次登录,QQ号未绑定任何Discuz!用户
- B账号:已绑定Discuz! UID 12345,但
qq_unionid字段为空(v2升级用户) - C账号:已绑定UID 67890,且
qq_unionid字段有值(v3新绑定用户)
在source/plugin/qqlogin/qqlogin.class.php中log_debug()方法(第 45 行)取消注释,开启调试日志:
// public function log_debug($msg) { // file_put_contents(DISCUZ_ROOT.'data/qqlogin_debug.log', date('Y-m-d H:i:s')."\t".$msg."\n", FILE_APPEND); // }5.2 关键日志字段解读表
| 日志关键词 | 触发位置 | 正常值示例 | 异常含义 |
|---|---|---|---|
[AUTH] code= | get_access_token_by_code() | code=123abc456def | code为空说明前端未正确传递code参数 |
[TOKEN] expires_in=7200 | get_access_token_by_code() | expires_in=7200 | 若<7000,说明QQ返回了异常token(需检查App Key) |
[USER] unionid=EA1B2C3D4E5F6789 | sync_user_info() | unionid=EA1B2C3D4E5F6789 | 若为null,说明QQ未返回unionid(检查QQ互联后台是否开启“获取unionid”权限) |
[BIND] uid=12345 | bind_local_user() | uid=12345 | 若出现uid=0,说明用户绑定失败(检查pre_common_member表写入权限) |
[SYNC] synlogin=1 | qqlogin_synlogin() | synlogin=1 | 若为synlogin=-1,说明UC通信失败(回到2.1节检查UC) |
5.3 用curl模拟完整授权链验证(脱离浏览器)
在服务器终端执行以下命令,模拟A账号首次登录全流程(需替换YOUR_APP_ID、YOUR_REDIRECT_URI):
# 1. 获取授权码(需人工打开URL复制code) echo "请访问:https://graph.qq.com/oauth2.0/authorize?client_id=YOUR_APP_ID&response_type=code&scope=get_user_info&redirect_uri=YOUR_REDIRECT_URI" # 2. 用code换token(替换CODE_HERE) curl -X POST "https://graph.qq.com/oauth2.0/token?grant_type=authorization_code&client_id=YOUR_APP_ID&client_secret=YOUR_APP_KEY&code=CODE_HERE&redirect_uri=YOUR_REDIRECT_URI" \ -H "Content-Type: application/x-www-form-urlencoded" \ -s | grep -o "access_token=[^&]*" # 3. 用token拉取用户信息(替换ACCESS_TOKEN) curl -X GET "https://graph.qq.com/user/get_user_info?access_token=ACCESS_TOKEN&oauth_consumer_key=YOUR_APP_ID&openid=OPENID" \ -H "Content-Type: application/json" \ -s | python3 -m json.tool | grep -E "(unionid|nickname|figureurl_qq_2)"血泪经验:我曾在线上环境用此法发现QQ返回的
figureurl_qq_2是302 URL,但Discuz!原生逻辑未处理跳转,导致头像批量失效。从此养成了每次升级插件必跑三遍curl验证的习惯——不是信不过作者,是信不过网络中间件的随机性。希望帮到你。
本文还有配套的精品资源,点击获取