微信小程序登录注册流程
→ 返回 面试
微信小程序没有传统「用户名 + 密码注册页」的强制流程。平台侧通过 wx.login 静默换取用户唯一标识(openid);业务侧的「注册」通常是首次 openid 入库并补全资料(昵称、头像、手机号等),「登录」则是识别已有用户并下发业务 Token。
核心概念
| 概念 | 说明 |
|---|---|
| AppID / AppSecret | 小程序凭证;AppSecret 仅保存在服务端,禁止下发到客户端 |
| code | wx.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 + Token | isNewUser: 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 / unionid | sub / email 等 |
| 会话 | 业务自建 JWT | Cookie Session 或 JWT |
九、常见面试题
Q:wx.login 和 wx.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 策略。
相关笔记
- 前后端交互 — JWT、Session、401/403
- OAuth2与JWT — 后端签发与校验 Token
- 密码加密 — 若小程序外还有账号密码体系