微信小程序登录注册流程

返回 面试

微信小程序没有传统「用户名 + 密码注册页」的强制流程。平台侧通过 wx.login 静默换取用户唯一标识(openid);业务侧的「注册」通常是首次 openid 入库并补全资料(昵称、头像、手机号等),「登录」则是识别已有用户并下发业务 Token


核心概念

概念说明
AppID / AppSecret小程序凭证;AppSecret 仅保存在服务端,禁止下发到客户端
codewx.login 返回的临时登录凭证,约 5 分钟有效、一次性
openid用户在当前小程序下的唯一标识
unionid用户在同一微信开放平台账号下多个应用(小程序、App、公众号)的统一标识;需绑定开放平台且满足 unionid 获取条件
session_key会话密钥,用于解密早期敏感数据;不得下发前端、不得写日志
access_token服务端调用微信 API 的全局凭证(与 session_key 不同),按 appid+secret 获取并缓存

标准流程总览

小程序端                          微信服务器                    业务后端
   │                                  │                            │
   │  wx.login()                      │                            │
   │ ───────────────────────────────► │                            │
   │ ◄─────────────────────────────── │  返回 code                 │
   │                                  │                            │
   │  POST /api/auth/wx-login { code }                             │
   │ ─────────────────────────────────────────────────────────────► │
   │                                  │  code2Session(appid,secret,code)
   │                                  │ ◄────────────────────────── │
   │                                  │  openid, session_key, unionid?
   │                                  │                            │
   │                                  │         查库:openid 是否存在
   │                                  │         不存在 → 注册(insert)
   │                                  │         存在   → 登录(update 等)
   │                                  │         签发业务 JWT / Token
   │ ◄───────────────────────────────────────────────────────────── │
   │  { token, isNewUser, ... }       │                            │
   │                                  │                            │
   │  后续请求 Header: Authorization: Bearer <token>               │
   │ ─────────────────────────────────────────────────────────────► │

要点code 必须在服务端openid;前端只传 code,不传 AppSecret


一、静默登录(必做)

1. 小程序端

// app.js 或登录页
async function wxLogin() {
  const { code } = await wx.login();
  if (!code) throw new Error("wx.login 失败");
 
  const res = await wx.request({
    url: "https://api.example.com/api/auth/wx-login",
    method: "POST",
    data: { code },
  });
 
  const { token, refreshToken, isNewUser } = res.data;
  wx.setStorageSync("token", token);
  // isNewUser === true 时引导补全资料(头像昵称 / 手机号)
  return { token, isNewUser };
}

2. 服务端:code2Session

微信接口(HTTPS GET):

https://api.weixin.qq.com/sns/jscode2session
  ?appid=APPID
  &secret=SECRET
  &js_code=CODE
  &grant_type=authorization_code

成功响应示例:

{
  "openid": "oXXXX",
  "session_key": "xxx",
  "unionid": "uXXXX",
  "errcode": 0
}

常见错误:40029 code 无效(过期或重复使用)、40163 code 已被使用、45011 频率限制。

3. 业务层:登录 vs 注册

步骤注册(新用户)登录(老用户)
依据库中无该 openid库中已有该 openid
动作INSERT user(openid, unionid?, ...)UPDATE last_login_at
返回isNewUser: true + TokenisNewUser: false + Token
前端跳转完善资料 / 绑定手机进入首页

session_key 建议加密存 Redis(key 如 wx:session:{openid},TTL 与微信会话策略一致),供后续解密或手机号接口使用;不要session_key 返回给小程序。


二、获取用户资料(昵称、头像)

2021 年后 wx.getUserInfo 不再弹窗授权;需用户主动点击

方式说明
wx.getUserProfile用户点击按钮后调用,返回加密或明文昵称头像(视基础库版本)
头像昵称填写能力<button open-type="chooseAvatar"> + input type="nickname",用户手动填写,合规且常用

推荐流程:静默登录拿到 Token 后,若 isNewUser 或资料不全,再引导用户点击完善资料,将昵称头像 POST 到 /api/user/profile 更新数据库。


三、手机号绑定(常见「注册完成」条件)

当前推荐:手机号快速验证组件

按钮:

<button open-type="getPhoneNumber" bindgetphonenumber="onGetPhoneNumber">
  授权手机号
</button>
async onGetPhoneNumber(e) {
  if (e.detail.errMsg !== "getPhoneNumber:ok") return;
  const phoneCode = e.detail.code; // 动态令牌,非明文手机号
  await wx.request({
    url: "https://api.example.com/api/auth/bind-phone",
    method: "POST",
    header: { Authorization: "Bearer " + wx.getStorageSync("token") },
    data: { phoneCode },
  });
}

服务端调用(需先取 access_token):

POST https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=ACCESS_TOKEN
Body: { "code": "phoneCode" }

返回 phone_info.phoneNumber 等,入库并完成「注册闭环」。

旧方案(了解即可)

encryptedData + iv + 服务端 session_key 本地 AES 解密 —— 面试可能问到,新项目应使用 getPhoneNumber 返回的 code + 服务端 API


四、后端 Token 设计

小程序适合 无状态 JWT(与 Cookie Session 对比见 JWT(JSON Web Token)详解)。

字段建议
sub业务 userId
openid可选,便于审计
过期access 2h ~ 7d;refresh 更长
存储前端 wx.setStorageSync;敏感操作校验 Token

签发与校验可参考 OAuth2与JWT

// 伪代码:wx-login 接口
@PostMapping("/api/auth/wx-login")
public WxLoginResponse login(@RequestBody WxLoginRequest req) {
    WxSession session = weChatClient.code2Session(req.getCode());
    User user = userRepo.findByOpenid(session.getOpenid())
        .orElseGet(() -> userRepo.save(newUser(session)));
    String token = jwtProvider.generate(user);
    return new WxLoginResponse(token, user.isNew());
}

五、多端 Token 策略

先区分两类 Token,避免和上文「业务 JWT」混淆:

Token持有方是否「每端一个」
微信 access_token仅服务端整个 AppID 共用一个(调微信 API),与用户、设备无关
业务 accessToken / refreshToken小程序 / Web / App每次登录签发一对;同一用户多端通常各持有一份

常见设计

同一套认证服务、同一套 JWT 规则,不同端走不同登录入口,最终统一落到 userId

小程序  wx-login(code)     ──┐
Web     password / OAuth   ──┼──► 同一 jwtProvider.generate(user)
App     手机号 / 第三方      ──┘
用户 A 小程序登录  →  accessToken₁ + refreshToken₁
用户 A Web 登录    →  accessToken₂ + refreshToken₂
用户 A iOS 登录    →  accessToken₃ + refreshToken₃
问题结论
各端 Token 算法必须不同?,同一密钥/公钥、同一校验逻辑即可
同一用户在小程序和 Web 的 Token 相同?通常否,各次登录各签一份
后端如何识别用户?JWT 的 sub = userId;openid 仅小程序登录时使用,账号打通靠 unionid / 手机号

可选 Claims

{
  "sub": "1001",
  "platform": "miniprogram",
  "client_id": "wx-mini-xxx"
}

用于分端过期时间、限流、审计;不强制拆成多套 Token 体系。

Refresh Token 按端隔离(推荐)

accessToken 可无状态 JWT;refreshToken 存 Redis,便于单端登出、踢设备:

refresh_token:{token} → { userId, platform, deviceId }

小程序登出只删该端 refresh,不影响 Web 仍在线。实现参考 Refresh Token 服务(Redis 存储)

与 openid 的关系

  • openid:用户 × 小程序 AppID,不同小程序 openid 不同。
  • 业务 Token:用户 × 登录会话,绑定 userId
  • 多端账号合并后,Token 仍指向同一 userId,但各端各自持有一份 Token 对。

六、access_token 缓存(服务端)

调用手机号、发送订阅消息等接口前,需 小程序 access_token

GET https://api.weixin.qq.com/cgi-bin/token
  ?grant_type=client_credential
  &appid=APPID
  &secret=SECRET
要点说明
有效期7200s,需集中缓存(Redis),避免多实例重复刷新
刷新过期前提前刷新;失败重试需退避
安全AppSecret 仅配置在服务端环境变量

七、安全与合规

风险防护
AppSecret 泄露仅服务端;轮换 secret;禁止打进小程序包
code 被截获HTTPS;code 一次性且短效;尽快服务端兑换
session_key 泄露不下发前端;Redis 加密存储;日志脱敏
Token 被盗短过期 + refresh;关键操作二次验证(手机号)
伪造 openid必须走微信 code2Session,不可信任客户端传的 openid

八、与 Web OAuth2 的对比(面试常问)

维度微信小程序Web OAuth2 授权码模式
用户感知可静默 wx.login通常跳转授权页
凭证临时 code授权码 code
换票jscode2session → openid换 access_token
身份标识openid / unionidsub / email 等
会话业务自建 JWTCookie Session 或 JWT

九、常见面试题

Q:wx.loginwx.getUserProfile 有什么区别?
A:wx.login 静默换取 登录态 code,用于换 openid,不涉及用户点击;getUserProfile 需用户点击,用于获取昵称头像,不能替代登录。

Q:openid 和 unionid 区别?
A:openid 单应用唯一;unionid 同一开放平台下多应用统一。做跨小程序/ App 账号打通用 unionid。

Q:为什么 code 不能在前端直接换 openid?
A:换票需要 AppSecret,放前端会泄露;必须由后端调用 code2Session

Q:首次打开小程序算注册还是登录?
A:微信侧无注册概念;业务上首次 openid 入库即「注册」,之后为「登录」。

Q:Token 存哪里?
A:常用 wx.setStorageSync;注意 XSS 风险较低但设备共享场景需考虑退出登录;勿存 session_key

Q:如何防止重复注册?
A:对 openid(或 unionid)建唯一索引,登录接口用 upsert / findOrCreate 保证幂等。

Q:小程序和 Web 各有一套 Token 吗?
A:通常同一 JWT 体系,但每次登录各签一份;微信 access_token 是 AppID 级全局凭证,与业务 Token 无关。详见 五、多端 Token 策略


相关笔记