3079 字
约 10 分钟
3
微信 SDK 扫码登录业务文档

微信 SDK 扫码登录业务文档

1. 文档说明

本文说明项目如何通过微信 SDK 完成用户身份认证、绑定本地用户和建立登录态,同时说明当前源码与完整的 PC 网站扫码登录之间的差异。

先给出结论:当前项目已经实现了“后端接收微信 OAuth2 回调并完成本地登录”,但没有实现完整的“前端生成扫码授权地址、展示二维码、扫码后回调”的闭环。代码中的 wx_open 命名也比较宽泛,从实际使用的 SDK 类型和字段来看,它更接近微信公众号网页 OAuth2 授权,而不一定是微信开放平台网站应用的 PC 扫码登录。

2. 业务目标

用户不需要在项目中重新注册账号和密码,可以使用微信身份登录项目。系统需要完成三件事:

  1. 让微信确认用户身份。
  2. 将微信身份和项目本地用户绑定起来。
  3. 使用项目自己的登录态保护后续业务接口。

微信只负责“认证用户是谁”,项目仍然负责用户角色、封禁状态、Session 和业务权限。

3. 两种微信登录场景

3.1 微信公众号网页授权

适用场景:用户在微信内打开项目网页,点击授权后登录。

典型授权地址是:

https://open.weixin.qq.com/connect/oauth2/authorize

常见作用域包括:

  • snsapi_base:静默获取基础身份信息,通常不弹授权确认页。
  • snsapi_userinfo:用户同意后获取昵称、头像等信息。

3.2 微信开放平台网站扫码登录

适用场景:用户在 PC 浏览器打开网站,使用微信扫描网页二维码登录。

典型授权地址是:

https://open.weixin.qq.com/connect/qrconnect

常见请求参数如下:

appid=开放平台网站应用 AppID
redirect_uri=后端回调地址
response_type=code
scope=snsapi_login
state=随机状态值

两种流程后半段都需要后端用临时 code 换取访问令牌,再获取微信用户信息;区别主要在账号类型、授权地址、作用域和平台配置。

4. 当前项目的实现边界

当前源码包含:

  • GET /api/user/login/wx_open 回调接口。
  • wx.open.appIdwx.open.appSecret 配置项。
  • 使用 wx-java-mp-spring-boot-starter 提供的 WxMpService
  • 通过 OAuth2 服务用 code 换取 access_token 和微信用户信息。
  • 根据 unionId 查询或创建本地用户。
  • 将本地用户写入项目 Session。

当前源码没有找到:

  • 前端生成并跳转微信授权地址的代码。
  • PC 端二维码展示页面。
  • state 的生成和校验逻辑。
  • 独立的扫码登录轮询或 WebSocket 通知机制。
  • 完整的网站应用扫码登录回调页面。

因此,当前项目不是启动后就能直接使用的完整微信扫码登录产品,而是已经写好了后端回调和本地账号绑定的核心部分。

5. 当前源码调用链

sequenceDiagram
    participant U as 用户浏览器
    participant W as 微信平台
    participant C as UserController
    participant S as UserService
    participant DB as MySQL

    U->>W: 打开授权地址并扫码/授权
    W-->>U: 回调地址附带临时 code
    U->>C: GET /api/user/login/wx_open?code=...
    C->>W: 使用 code 换 access_token
    W-->>C: 返回 access_token
    C->>W: 获取微信用户信息
    W-->>C: 返回 unionId、openid、昵称、头像
    C->>S: userLoginByMpOpen(userInfo, request)
    S->>DB: 根据 unionId 查询本地用户
    alt 用户不存在
        S->>DB: 创建本地用户
    end
    S->>S: 检查是否被封禁
    S->>C: 写入项目 Session
    C-->>U: 返回 LoginUserVO

6. 代码实现说明

6.1 配置微信应用

配置文件中的相关内容是:

wx:
  open:
    appId: xxx
    appSecret: xxx

WxOpenConfig 使用 @ConfigurationProperties(prefix = "wx.open") 读取配置,并在第一次调用 getWxMpService() 时创建微信 SDK 服务对象:

读取 appId 和 appSecret
  -> 创建 WxMpDefaultConfigImpl
  -> 写入 appId 和 secret
  -> 创建 WxMpServiceImpl
  -> 缓存服务对象

appSecret 只能保存在后端,不能写进前端代码或二维码参数中。

6.2 接收微信回调

当前回调接口是:

GET /api/user/login/wx_open?code=微信返回的临时凭证

控制器从请求中获取 code,然后调用:

accessToken = wxService.getOAuth2Service().getAccessToken(code);
WxOAuth2UserInfo userInfo = wxService.getOAuth2Service()
        .getUserInfo(accessToken, code);

code 具有短时有效、只能使用一次等特点。后端拿到它后立即向微信换取访问令牌,前端不应该自行使用 appSecret 调用微信接口。

6.3 获取微信身份

当前代码要求微信返回以下两个标识:

String unionId = userInfo.getUnionId();
String mpOpenId = userInfo.getOpenid();

两者的业务含义不同:

标识 含义 当前用途
unionId 同一开放平台主体下较稳定的用户标识 查询和绑定本地用户
openid 用户在当前公众号或应用下的标识 保存到 mpOpenId

当前项目使用 unionId 作为本地账号匹配依据,并把 openid 保存到用户表中。若微信没有返回其中任意一个值,控制器会认为登录失败。

6.4 绑定本地用户

UserServiceImpl.userLoginByMpOpen() 的处理规则是:

根据 unionId 查询 user 表
  -> 用户存在且被封禁:拒绝登录
  -> 用户存在且正常:直接登录
  -> 用户不存在:创建本地用户
  -> 写入项目 Session

首次登录时,代码会保存:

unionId
mpOpenId
userName
userAvatar

项目不会保存微信密码,也不会把微信 access_token 当作本项目的长期登录凭证。

6.5 建立项目登录态

登录完成后,代码执行:

request.getSession().setAttribute(USER_LOGIN_STATE, user);

其中 Session 键是:

user_login

接口返回脱敏后的 LoginUserVO。后续请求由浏览器自动携带 JSESSIONID,项目再从 Session 中获取用户 id,并回 MySQL 查询最新用户状态。

因此,微信登录和项目登录是两层关系:

微信 OAuth2 认证
      ↓
unionId 绑定本地 user
      ↓
项目 Session 登录
      ↓
项目权限校验

7. 完整 PC 扫码登录建议流程

如果目标是“电脑网页显示二维码,用户用微信扫描登录”,建议补齐下面的流程。

7.1 微信平台准备

根据登录场景完成对应配置:

  1. 注册微信开放平台账号或符合要求的公众号主体。
  2. 创建网站应用或配置公众号网页授权能力。
  3. 获取真实的 AppIDAppSecret
  4. 配置授权域名、回调域名和精确回调地址。
  5. 生产环境使用 HTTPS。
  6. 按微信平台当前规则完成主体认证或应用审核。

具体资质要求会随账号类型和平台规则变化,不能用普通字符串替代真实凭证。

7.2 前端发起授权

PC 网站扫码登录通常由后端生成带 state 的授权地址,前端跳转到微信:

https://open.weixin.qq.com/connect/qrconnect?
appid=APP_ID
&redirect_uri=ENCODED_CALLBACK_URL
&response_type=code
&scope=snsapi_login
&state=RANDOM_STATE
#wechat_redirect

二维码页面由微信提供,SDK 的作用主要是帮助后端完成换取令牌和获取用户信息,不是替代整套前端登录页面。

7.3 回调并完成登录

微信回调时,后端至少需要处理:

校验 state
  -> 校验 code 非空
  -> 用 code 换 access_token
  -> 获取微信用户信息
  -> 根据 unionId 查询本地用户
  -> 创建或更新本地用户
  -> 写入 Session 或签发项目 Token
  -> 跳转回前端页面

如果使用前后端分离项目,回调接口可以在服务端完成登录后重定向到前端,并由前端调用 /api/user/get/login 确认当前登录用户。

8. 当前源码需要修正或补充的地方

8.1 首次建用户可能违反数据库约束

userLoginByMpOpen() 创建新用户时没有设置 userAccountuserPassword,但 create_table.sql 中这两个字段是 NOT NULL

因此,首次微信登录可能插入失败。可选处理方式包括:

  • 允许微信用户的账号密码字段为空。
  • 为微信用户生成内部唯一账号和随机密码。
  • 将登录方式拆成独立的第三方账号绑定表。

更适合长期维护的方案是增加第三方账号表,例如:

user
  -> 用户基本信息

user_social_account
  -> userId
  -> platform = wechat
  -> unionId
  -> openId

8.2 unionId 应增加唯一约束

当前数据库只有 unionId 普通索引。synchronized (unionId.intern()) 只能防止同一个 JVM 内的并发重复创建,无法覆盖多实例部署。

生产环境应增加唯一约束,并在插入冲突时重新查询用户:

unique key uk_user_union_id (unionId)

8.3 增加 state 防止伪造回调

当前回调只接收 code,没有校验 OAuth2 的 state。完整流程应该由项目生成随机、短时有效的 state,并在回调时校验它,防止登录 CSRF 和回调串线。

8.4 核对 SDK 的用户信息参数

当前代码调用:

getUserInfo(accessToken, code)

接入时应根据项目实际使用的 wx-java 版本核对该方法第二个参数。部分版本中该参数表示语言,例如 zh_CN,而不是 OAuth2 的 code。如果方法签名确实要求语言参数,应改为对应语言值。

8.5 补齐前端授权入口

当前仓库中能看到后端回调,但没有完整的微信授权地址生成和前端扫码页面。因此还需要补充:

  • 登录按钮。
  • 授权地址生成接口或前端授权跳转。
  • 回调后的前端页面处理。
  • 登录成功后刷新当前用户状态。
  • 登录失败和取消授权的提示。

9. 异常处理

异常场景 业务表现 建议处理
code 过期或重复使用 换取令牌失败 重新发起授权
AppID 或密钥错误 微信接口返回错误 检查环境变量和平台配置
回调域名未配置 微信无法正常回调 配置授权域名和 HTTPS
未返回 unionId 项目拒绝登录 检查账号绑定和授权范围
本地用户被封禁 禁止登录 返回明确的封禁提示
MySQL 创建用户失败 登录失败 检查非空字段、唯一约束和事务
Session Cookie 未保存 登录后仍未登录 检查跨域、Cookie 和代理配置

当前控制器把微信 SDK 异常统一转换成“登录失败,系统错误”,生产环境可以记录内部错误码,同时向用户返回更容易理解的提示。

10. 验收标准

完成完整扫码登录后,应至少验证:

  1. 未登录用户可以打开微信授权页面或扫码页面。
  2. 微信回调地址和域名配置正确。
  3. code 只能成功使用一次。
  4. 已存在的 unionId 不会重复创建本地用户。
  5. 首次登录可以成功写入本地用户。
  6. 被封禁用户无法通过微信登录。
  7. 登录成功后访问 /api/user/get/login 能返回当前用户。
  8. 注销后 Session 被清除,受保护接口无法继续访问。
  9. appSecret 不会出现在前端、日志和 URL 中。
  10. 重放旧 state 或伪造 state 会被拒绝。

11. 源码定位

微信 SDK 扫码登录业务文档
http://clxhxhhr.top/posts/436/
作者
clxstart
发布于
2026-09-04
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。