简介
本文件聚焦 DouPHP 框架中各社交平台(微信、QQ、Google、Amazon)的 OAuth 登录实现差异,对比其 API 调用方式、参数配置、错误处理机制,并说明微信公众号与开放平台的区别处理、移动端与 PC 端适配方案。同时提供各平台配置参数说明、安全注意事项与性能优化建议,帮助开发者快速定位问题并正确集成。
项目结构
DouPHP 将第三方登录以“插件”形式组织,每个平台一个独立插件目录,包含 Provider(元数据与入口)、Service(业务逻辑)、可选 SDK 或脚本。统一通过 SnsLoginService 完成“绑定/自动注册/登录”的最终落地。
graph TB
subgraph "前端入口"
UI["用户点击登录"]
end
subgraph "插件层"
WX["微信登录插件<br/>WxloginProvider / WxloginService"]
QQ["QQ 登录插件<br/>QqService"]
GGL["Google 登录插件<br/>GoogleProvider / GoogleService"]
AMZ["Amazon 登录入口<br/>login.php"]
end
subgraph "统一协调"
SNS["SnsLoginService<br/>resolve()"]
end
UI --> WX
UI --> QQ
UI --> GGL
UI --> AMZ
WX --> SNS
QQ --> SNS
GGL --> SNS
AMZ --> SNS
核心组件
- 微信登录插件(公众号/开放平台)
- 通过 UA 判断是否在微信内,选择公众号授权页或开放平台扫码页;分别使用不同的 appid/appsecret。
- 回调后校验 state,换取 access_token/openid,再拉取用户信息,最终交由 SnsLoginService 处理绑定/登录。
- QQ 登录插件
- 基于官方 SDK 完成 code 换 token、获取 openid 和用户信息,统一走 SnsLoginService。
- Google 登录插件
- 标准 OAuth 2.0/OpenID Connect:构造授权 URL、state 校验、code 换 token、Bearer 获取 userinfo,统一走 SnsLoginService。
- Amazon 登录入口
- 当前实现实际跳转至微信授权流程(用于兼容历史路径),在微信客户端与非微信客户端选择不同的授权地址。
架构总览
所有平台登录最终汇聚到统一的 SnsLoginService.resolve(),根据是否已绑定、是否已登录、配置项决定直接登录、跳转绑定页或自动注册。
sequenceDiagram
participant U as "用户浏览器/小程序"
participant P as "平台插件(微信/QQ/Google)"
participant O as "平台OAuth服务"
participant C as "SnsLoginService"
participant R as "路由/页面"
U->>P : 发起start()
P->>O : 重定向到授权页(state)
O-->>U : 回调带code/state
U->>P : finish(code,state)
P->>O : 用code换token/取用户信息
O-->>P : 返回openid/unionid/昵称等
P->>C : resolve(sns, userProfile, loginIdMode, nobind)
alt 已绑定且未登录
C-->>R : 跳转绑定页
else 已绑定且已登录
C-->>R : 直接登录并跳转用户中心
else 未绑定且允许nobind
C-->>R : 自动注册并登录
end
详细组件分析
微信登录(公众号 vs 开放平台)
- 关键差异
- 公众号(移动/微信内):UA 含 MicroMessenger,使用公众号 appid/appsecret,授权 scope=snsapi_userinfo。
- 开放平台(PC 扫码):非微信 UA,使用开放平台 appid/appsecret,授权 scope=snsapi_login。
- 回调处理
- 校验 state,换取 access_token/openid,必要时检查 unionid(按配置)。
- 构建 sns 对象(group=wxlogin, apptype=weixin),交由 SnsLoginService 处理。
- 移动端与 PC 端适配
- 同一插件通过 UA 自动区分,无需前端额外判断。
- 小程序侧补充
- 小程序登录走 api/controller/user/WeixinController,使用 jscode2session,与 PC 插件流程分离。
flowchart TD
Start(["开始"]) --> UA{"是否微信UA?"}
UA -- 是 --> MP["公众号授权<br/>scope=snsapi_userinfo"]
UA -- 否 --> OP["开放平台扫码<br/>scope=snsapi_login"]
MP --> CB["回调校验state+code"]
OP --> CB
CB --> Token["换取access_token/openid"]
Token --> Info["拉取用户信息(含unionid可选)"]
Info --> Resolve["SnsLoginService.resolve()"]
Resolve --> End(["结束"])
QQ 登录
- 授权与回调
- start() 生成 state 并存入会话,构造 graph.qq.com 授权 URL。
- finish() 加载 SDK,调用 qq_callback() 获取 accessToken 与 openid,再取用户信息。
- 数据映射
- 将 nickname、头像、性别等映射为统一 sns 结构,交由 SnsLoginService 处理。
- 配置
- 需要 appid 与 appkey,并在插件配置中设置回调地址。
sequenceDiagram
participant U as "用户"
participant Q as "QqService"
participant SDK as "QQ SDK"
participant S as "SnsLoginService"
U->>Q : start()
Q-->>U : 重定向到QQ授权页
U-->>Q : 回调finish(code)
Q->>SDK : qq_callback() + get_openid()
SDK-->>Q : accessToken, openid
Q->>SDK : get_user_info()
SDK-->>Q : 用户信息
Q->>S : resolve(sns, profile, 'openid', false)
S-->>U : 登录/绑定/注册结果
Google 登录
- 授权与回调
- start() 生成 state 存入 Session,构造 accounts.google.com 授权 URL(scope=openid email profile)。
- finish() 校验 state,用 code 换 access_token,再用 Bearer 获取 userinfo(sub 作为唯一标识)。
- 数据映射
- 将 name/picture 等映射为统一 sns 结构,sex 默认空值。
- 配置
- 需要 client_id 与 client_secret,回调地址需在 Google Cloud Console 中注册。
sequenceDiagram
participant U as "用户"
participant G as "GoogleService"
participant GO as "Google OAuth"
participant S as "SnsLoginService"
U->>G : start()
G-->>U : 重定向到Google授权页
U-->>G : 回调finish(code,state)
G->>GO : POST /token (code)
GO-->>G : access_token
G->>GO : GET /userinfo (Bearer)
GO-->>G : {sub, name, picture...}
G->>S : resolve(sns, profile, 'openid', false)
S-->>U : 登录/绑定/注册结果
Amazon 登录入口(当前实现)
- 行为说明
- 该入口实际跳转到微信授权流程,并根据 UA 选择公众号或开放平台授权页。
- 适用于历史兼容场景,不建议在新项目中直接使用。
- 建议
- 如需接入 Amazon OAuth,应新建独立插件并按 Google/QQ 模式实现。
依赖关系分析
- 插件与服务
- 微信/QQ/Google 插件均依赖 SnsLoginService 进行最终的用户绑定/登录决策。
- 微信插件还依赖配置项(param.login_mode、param.loginid_mode)控制行为。
- 外部依赖
- 微信:open.weixin.qq.com(授权)、api.weixin.qq.com(token/userinfo)。
- QQ:graph.qq.com(授权)、官方 SDK。
- Google:accounts.google.com(授权)、oauth2.googleapis.com(token)、www.googleapis.com(userinfo)。
graph LR
WX["微信插件"] --> SNS["SnsLoginService"]
QQ["QQ插件"] --> SNS
GGL["Google插件"] --> SNS
WX --> WXAPI["微信API"]
QQ --> QQAPI["QQ接口/SDK"]
GGL --> GAPI["Google OAuth API"]
性能考虑
- 网络请求
- 微信/Google 均涉及多次 HTTP 调用(授权、换 token、取用户信息),建议在服务器侧启用连接复用与超时控制。
- 状态校验
- 使用 state 防 CSRF,避免重复计算与无效回调。
- 缓存策略
- 对频繁访问的用户信息可短期缓存(注意隐私与过期时间)。
- 限流与重试
- 对第三方 API 失败做指数退避重试,避免雪崩。
故障排查指南
- 常见错误与定位
- state 不匹配:检查 Session 是否被清理或跨域导致丢失。
- 无法获取 code:确认回调地址与平台后台一致,且未被拦截。
- 无法获取用户信息:检查 token 是否有效、scope 是否足够。
- unionid 缺失:当配置要求 unionid 时,需确保平台返回 unionid。
- 日志与审计
- 微信登录失败会记录审计日志(如 code 无效、unionid 缺失)。
- 管理员登录失败有明确审计分类,便于排查。
结论
DouPHP 通过插件化设计将不同平台的 OAuth 登录统一到 SnsLoginService,简化了多平台接入与维护。微信插件支持公众号与开放平台双模式,QQ 与 Google 遵循各自平台规范。Amazon 入口当前为历史兼容实现。建议新项目优先采用微信/QQ/Google 的标准插件,并严格遵循配置与安全最佳实践。
附录:配置参数与常见问题
微信登录(插件)
- 配置字段
- APPID(微信开放平台):用于 PC 扫码登录。
- APPSECRET(微信开放平台):与开放平台 APPID 配对。
- APPID(微信公众号):用于微信内自动登录。
- APPSECRET(微信公众号):与公众号 APPID 配对。
- 常见问题
- 回调地址不一致:需在对应平台后台配置完全一致的回调地址。
- unionid 缺失:若配置要求 unionid,请确保平台返回 unionid。
- UA 识别异常:确认请求头中包含 MicroMessenger 以进入公众号流程。
QQ 登录(插件)
- 配置字段
- appid:QQ 开放平台应用 ID。
- appkey:与 appid 配对的密钥。
- 常见问题
- SDK 初始化失败:检查全局 connect_inc 配置是否正确注入。
- 权限不足:确保 scope 包含 get_user_info。
Google 登录(插件)
- 配置字段
- Client ID:Google OAuth 2.0 客户端 ID。
- Client Secret:与 Client ID 配对的密钥。
- 常见问题
- 回调地址未注册:需在 Google Cloud Console 中添加回调地址。
- scope 不足:确保包含 openid、email、profile。
Amazon 登录(入口)
- 现状说明
- 当前实现跳转至微信授权流程,并非真正的 Amazon OAuth。
- 建议
- 如需接入 Amazon OAuth,请按 Google/QQ 模式新建插件。
小程序微信登录(API)
- 流程说明
- 前端 wx.login 获取 code,后端调用 jscode2session 获取 openid/session_key,再完成绑定/登录。
- 常见问题
- code 无效:确认 code 仅一次使用且在有效期内。
- unionid 缺失:当配置要求 unionid 时需确保平台返回。