文档目录
微信登录插件

简介

本技术文档围绕 DouPHP 的“微信登录”插件,系统阐述微信公众平台 OAuth2.0 授权流程在本项目中的落地实现。重点覆盖公众号配置、AppID/AppSecret 设置、授权码获取、用户信息拉取、OpenID/UnionID 映射、头像与昵称处理等关键环节;并给出网页授权登录、小程序登录、APP 登录等不同场景的实现方案与注意事项。同时说明安全机制(state 校验、token 刷新策略、隐私保护)以及 UnionID 多端统一账号的高级能力。

项目结构

微信登录相关代码主要分布在以下位置:

  • 插件入口与业务服务:plugin/wxlogin
  • 第三方登录通用协调服务:_'/module/user/core/service/user/SnsLoginService.php
  • 小程序 API 登录控制器:api/controller/user/WeixinController.php
  • 基础配置:config/config.php
graph TB
subgraph "插件层"
P["WxloginProvider<br/>插件接入点"]
S["WxloginService<br/>OAuth2.0 流程"]
end
subgraph "核心服务层"
C["SnsLoginService<br/>统一登录协调"]
end
subgraph "小程序API层"
A["WeixinController<br/>jscode2session / 绑定"]
end
subgraph "配置"
CFG["config.php<br/>应用密钥等"]
end
P --> S
S --> C
A --> C
CFG -.-> S
CFG -.-> A

核心组件

  • WxloginProvider:插件对外暴露的接入点,负责声明插件元数据(名称、描述、版本、分组、客户端类型、配置项),并将 start/finish 回调委托给 WxloginService。
  • WxloginService:实现微信公众号/开放平台两套 AppID 的自动选择、生成 state、构造授权 URL、回调校验 state、换取 access_token 与 openid、拉取用户信息、组装 SNS 数据并交由 SnsLoginService 完成登录/绑定/注册逻辑。
  • SnsLoginService:统一的第三方登录协调器,按优先级处理:已关联用户直接登录、当前登录用户绑定、未登录且允许免绑时自动注册、否则进入绑定流程。
  • WeixinController(小程序API):小程序侧通过 jscode2session 获取 openid/session_key,结合 unionid/openid 进行用户查找或绑定,支持手机号绑定与自动注册。

架构总览

下图展示了网页端(公众号/开放平台)与小程序端的整体登录链路。

sequenceDiagram
participant U as "用户浏览器/小程序"
participant P as "WxloginProvider"
participant S as "WxloginService"
participant WX as "微信服务器"
participant C as "SnsLoginService"
Note over U,P : 网页授权登录PC扫码/公众号内
U->>P : 触发开始
P->>S : start()
S->>S : resolveAppCredentials()<br/>生成state并写入Session
S-->>U : 重定向到微信授权页
U->>WX : 授权并回调
WX-->>S : finish(code, state)
S->>S : 校验state
S->>WX : 换取access_token/openid
WX-->>S : token信息
S->>WX : 拉取用户信息
WX-->>S : 用户资料
S->>C : resolve(sns, userProfile, loginIdMode, nobind)
C-->>U : 跳转用户中心/绑定页面
Note over U,C : 小程序登录API
U->>U : wx.login() 获取 code
U->>C : 调用 api/user/weixin/login(code, phone?)
C->>WX : jscode2session
WX-->>C : openid/unionid/session_key
C->>C : 查找/绑定/注册
C-->>U : 返回登录态/绑定结果

详细组件分析

WxloginProvider(插件接入点)

  • 职责
    • 声明插件标识、名称、描述、版本、分组、支持的客户端类型。
    • 提供配置项:开放平台 AppID/AppSecret、公众号 AppID/AppSecret。
    • 将 start/finish 请求委派给 WxloginService。
  • 关键点
    • pluginId 固定为 'wxlogin'。
    • meta 中定义四组配置字段,用于后台管理界面展示与保存。
    • start/finish 透传参数至服务层,保持插件薄封装。

WxloginService(OAuth2.0 流程)

  • 职责
    • 根据 UA 判断是公众号还是开放平台,分别使用对应 AppID/AppSecret。
    • 生成随机 state 并写入 Session,构建微信授权链接。
    • 回调时校验 state,换取 access_token 与 openid,拉取用户信息。
    • 组装 sns 数据(group/apptype/openid/unionid/nickname/avatar/sex),交给 SnsLoginService 处理登录/绑定/注册。
  • 关键流程
    • 授权开始:start()
      • 解析凭据:resolveAppCredentials()
      • 生成 state:md5(uniqid(mt_rand())) 存入 Session
      • 构造回调地址:index.php?route=plugin/wxlogin/finish
      • 根据 isMp 决定 scope 与授权 URL(snsapi_userinfo vs snsapi_login)
    • 回调处理:finish()
      • 校验 state:Session 中期望值与回调 state 一致
      • 换取 token:fetchTokenOpenid()
      • 拉取用户信息:fetchUserinfo()
      • 组装 sns:nickname 非法字符过滤、性别标准化、头像直链
      • 决策登录:resolve(loginIdMode='openid'|'unionid', nobind)
  • 错误处理
    • 配置缺失、state 不匹配、无法获取 token/用户信息均抛出领域异常并引导回用户页。
flowchart TD
Start(["开始"]) --> Resolve["解析AppID/Secret<br/>按UA选择公众号或开放平台"]
Resolve --> GenState["生成state并写入Session"]
GenState --> BuildURL["构造微信授权URL"]
BuildURL --> Redirect["重定向到微信授权页"]
Redirect --> Callback["回调finish(code,state)"]
Callback --> CheckState{"state校验通过?"}
CheckState -- 否 --> ErrState["抛出异常并返回用户页"]
CheckState -- 是 --> GetToken["换取access_token/openid"]
GetToken --> GetUser["拉取用户信息"]
GetUser --> Assemble["组装sns数据<br/>nickname/头像/性别"]
Assemble --> Decide["调用SnsLoginService.resolve<br/>登录/绑定/注册"]
Decide --> End(["结束"])

SnsLoginService(统一登录协调)

  • 职责
    • 接收来自各 Provider 的 sns 数据,按优先级处理:
      1. 若 openid/unionid 已关联用户,则直接登录并跳转用户中心;
      2. 若当前已登录用户,则将 sns 绑定到该用户;
      3. 若允许免绑且未登录,则自动注册新用户并登录;
      4. 否则将 sns 数据写入 Session,跳转到绑定页面。
  • 关键点
    • loginIdMode 支持 'openid' 或 'unionid',影响查找已关联用户的策略。
    • 自动注册时生成临时邮箱与密码,写入 user_sns 关联,并记录分销关系。
classDiagram
class SnsLoginService {
+resolve(sns, userProfile, loginIdMode, nobind) string
-insertUserSns(userId, sns) void
-autoRegister(sns) int
}

小程序登录(API)

  • 职责
    • 接收前端 wx.login() 返回的 code,调用 jscode2session 获取 openid/session_key(可选 unionid)。
    • 根据 loginid_mode 决定是否必须 unionid。
    • 支持手机号绑定与自动注册,最终返回登录态或绑定结果。
  • 关键点
    • ipRateLimit 防刷。
    • 当 unionid 缺失且要求 unionid 模式时,明确报错。
    • 绑定失败时记录审计日志并回滚事务。

依赖关系分析

  • 插件层依赖服务层:WxloginProvider -> WxloginService
  • 服务层依赖协调器:WxloginService -> SnsLoginService
  • 小程序 API 独立于网页插件:WeixinController -> 微信接口 + 数据库
  • 配置依赖:config.php 提供应用级密钥;插件配置由后台管理保存并通过 plugin() 读取
graph LR
P["WxloginProvider"] --> S["WxloginService"]
S --> C["SnsLoginService"]
A["WeixinController"] --> DB["数据库(user_sns/user)"]
S --> DB
CFG["config.php"] -.-> S
CFG -.-> A

性能与安全

  • 性能
    • 网络请求:微信授权与用户信息拉取均为外部 HTTP 调用,应关注超时与重试策略。
    • 数据库查询:user_sns 表以 openid/unionid 作为快速定位键,建议确保索引合理。
    • 会话状态:state 仅短期存储于 Session,避免持久化敏感信息。
  • 安全
    • state 校验:回调时必须严格比对 Session 中期望 state 与回调 state,防止 CSRF 攻击。
    • 输入校验:nickname 非法字符过滤,避免注入与显示异常。
    • 频率限制:小程序登录接口具备 IP 限流,防止暴力破解。
    • 隐私保护:头像直链由微信返回,服务端不缓存敏感字段;unionid 仅在需要时参与匹配。
    • 错误审计:登录失败、绑定异常等路径记录审计日志,便于追踪问题。

故障排查指南

  • 常见错误与定位
    • “无法获取微信授权信息”:检查回调 state 是否一致、code 是否过期、AppID/AppSecret 是否正确。
    • “无法获取 unionid”:确认公众号/开放平台已开启 UnionID 机制,且在必要模式下启用 unionid。
    • “请求微信接口失败”:检查网络连通性、证书验证、IP 白名单配置。
    • “账户已被锁定”:查看登录失败次数与锁定策略,必要时解锁。
  • 排查步骤
    • 核对插件配置:开放平台与公众号的 AppID/AppSecret 是否分别正确。
    • 检查回调地址:确保回调域名在微信后台配置。
    • 查看日志:审计日志与错误堆栈,定位具体失败阶段。
    • 复现最小用例:单独测试 code 换 token、token 换用户信息两个步骤。

结论

DouPHP 的微信登录插件采用清晰的插件-服务-协调器分层设计,既支持网页端(公众号/开放平台)OAuth2.0 授权,也兼容小程序 API 登录。通过 state 校验、UnionID 统一身份、严格的输入与错误处理,实现了安全可靠的第三方登录体验。开发者可基于此框架扩展更多社交登录渠道,并保持统一的登录流程与数据结构。

附录:开发指南与多端实现

网页授权登录(公众号/开放平台)

  • 配置
    • 在插件配置中填写开放平台 AppID/AppSecret(PC 扫码登录)与公众号 AppID/AppSecret(公众号内自动登录)。
  • 流程
    • 用户点击登录 -> 生成 state 并重定向到微信授权页 -> 回调校验 state -> 换取 token -> 拉取用户信息 -> 登录/绑定/注册。
  • 关键点
    • UA 判断:MicroMessenger 表示公众号环境,使用公众号 AppID;否则使用开放平台 AppID。
    • scope:公众号使用 snsapi_userinfo,开放平台使用 snsapi_login。
    • 回调地址:需与微信后台配置的回调域名一致。

小程序登录

  • 配置
    • 在系统配置中设置小程序 AppID/AppSecret。
  • 流程
    • 前端 wx.login() 获取 code -> 后端调用 jscode2session -> 获取 openid/session_key(可选 unionid)-> 查找/绑定/注册 -> 返回登录态。
  • 关键点
    • 支持手机号绑定与自动注册。
    • 当要求 unionid 模式但缺失时,明确报错。

APP 登录(概念性说明)

  • 若需支持 APP 端微信登录,通常采用微信开放平台的移动应用 OAuth2.0 流程。本项目当前插件聚焦网页与小程序,APP 端可扩展类似 WxloginService 的服务,复用 SnsLoginService 的统一协调逻辑。
  • 注意
    • 不同平台的 AppID/AppSecret 与回调地址需分别配置。
    • 遵循各平台的安全规范与权限申请。

安全机制要点

  • state 参数验证:回调时严格比对 Session 中的期望 state。
  • token 刷新:access_token 有效期有限,需在业务侧按需刷新并妥善存储。
  • 用户隐私保护:仅拉取必要字段,头像直链由微信返回,不缓存敏感信息。
  • 频率限制:小程序登录接口具备 IP 限流,防止滥用。

UnionID 与多端统一

  • 当配置 loginid_mode 为 unionid 时,系统优先按 unionid 匹配已关联用户,实现公众号、小程序、开放平台等多端账号统一。
  • 若 unionid 缺失且要求 unionid 模式,将提示管理员处理。
添加日期:2026-10-05