xnx_oauth OAuth2.0 统一登录
功能
标准 OAuth2.0 授权码流程,支持 QQ / GitHub / Google 等第三方登录(内置模板,可自定义 provider)
多 provider 注册机制:后台 kv 配置 + 其他插件静态注册(
OAuthService::registerProvider())完整授权流程:state 校验防 CSRF、PKCE(S256)支持、SSL 证书校验、原子写入头像
登录或绑定四分支:已绑定直接登录、已登录则绑定、邮箱匹配自动合并、其他拒绝并引导注册
个人中心绑定管理页 + 后台绑定记录列表(含筛选、分页、强制解绑)
字段映射支持嵌套字段(如
a.b),适配各平台差异的 userinfo 响应头像下载到本地
upload/avatar/{dir}/{uid}.png(仅当用户无自定义头像时)多语言(zh-cn / zh-tw / en-us)
文件结构
plugin/xnx_oauth/
├── conf.json
├── install.php / uninstall.php
├── setting.php # 后台配置 + 绑定记录管理
├── icon.png
├── model/
│ └── OAuthService.php # 核心服务(provider 注册、授权、绑定、头像下载)
├── route/
│ └── oauth.php # 前台路由:login / callback / bind-manage / unbind
├── admin/
│ └── view/htm/
│ ├── oauth_setting.htm # 后台 provider 配置页
│ └── oauth_bindlist.htm # 后台绑定记录列表
├── view/htm/
│ └── my_oauth.htm # 个人中心绑定管理页
└── hook/
├── model_inc_file.php # 自动加载 OAuthService
├── model_route_table_end.php # 注册 oauth 路由
├── index_route_case_end.php
├── admin_sidebar_end.htm # 后台侧栏入口
├── my_sidebar_nav_security_before.htm # 个人中心侧栏入口
├── user_login_submit_after.htm
├── user_create_submit_after.htm
├── lang_zh_cn_bbs.php
├── lang_zh_tw_bbs.php
└── lang_en_us_bbs.php
配置流程
后台「插件管理」启用 xnx_oauth
进入「插件设置」→「配置」→ 开启「启用 OAuth 登录」
选择要接入的平台(QQ / GitHub / Google 或自定义),填写 AppID / AppSecret、授权 / Token / UserInfo 三个 URL、scope、字段映射
如需 PKCE(如 Google),勾选
enable_pkce如需邮箱自动合并已有账号,开启
auto_merge_by_email测试:退出登录 → 访问登录页 → 点击第三方登录 → 完成授权回调跳转个人中心;或在个人中心「安全设置」点击「OAuth 绑定管理」进行绑定 / 解绑
扩展说明
OAuthService 通过 hook/model_inc_file.php 自动加载,可被其他插件调用以注册自定义 provider。
Service 类与方法签名
// 静态注册 provider(推荐在其他插件的 model_inc_file.php 或 hook 中调用)
OAuthService::registerProvider(array $config): void
// $config 关键字段:name, display_name, icon, color,
// authorize_url, token_url, userinfo_url, pre_userinfo_url(可选),
// appid, appkey, scope, enable, enable_pkce, field_mapping, extra_params
// 读取 provider
OAuthService::getProviders(): array
OAuthService::getProvider(string $name): ?array
OAuthService::init(): void // 合并 kv 配置与静态注册(同名后者覆盖并记录 xn_log 警告)
// 实例方法(通过 getInstance() 获取)
$svc = OAuthService::getInstance();
$svc->getAuthUrl(string $provider): array // ['ok'=>bool, 'url'=>string, 'message'=>string]
$svc->handleCallback($provider, $code, $state): array // 含 state 校验 + token 换取 + 用户信息映射 + 登录或绑定
$svc->loginOrBind($provider, $userInfo): array
$svc->bindAccount(int $uid, $provider, $userInfo): array
$svc->unbindAccount(int $uid, $provider): array // 解绑前会校验是否有其他登录方式
$svc->getUserBinds(int $uid): array
$svc->hasOtherLoginMethod(int $uid, string $excludeProvider = ''): bool
触发时机与参数
registerProvider()通常在其他插件的model_inc_file.phphook 中调用,确保OAuthService::init()之前完成注册同名 provider 后注册覆盖先注册,会写
xn_log('...', 'oauth_error')警告handleCallback()内部依次执行:state 校验 → PKCE 校验 →exchangeToken→getUserInfo(QQ 等会先调pre_userinfo_url取 openid)→mapUserInfo(按field_mapping映射 openid/unionid/nickname/avatar/email/gender)→loginOrBind数据表
xnx_oauth_bind字段含provider+openid唯一索引、provider+unionid普通索引,方便其他插件按 unionid 查询跨平台身份