前言
只要开始写一个微信小程序,必要的登录流程每次都要再理一下逻辑,记录一下思路,以便于后面自己食用。
大家有什么更好的思路可以分享~后面想创建一个学习小组,有兴趣的小伙伴可以联系我。
文章目录
- 前言
- 一、小程序微信授权登录
- 1.登录态检查(节点 B)
- 2.获取临时凭证 `code`(节点 D)
- 3. 开发者服务器换用户标识(节点 F-G)
- 4. 生成自定义登录态(节点 H)
- 5.登录态存储与使用(节点 J-L)
- 二、 需用户信息授权(如获取头像、昵称)
- 三、小程序获取手机号登录
- 1. 前置:获取临时登录凭证 code(节点 D)
- 2. 触发手机号授权(节点 E-F)
- 3. 获取加密手机号数据(节点 H)
- 4. 服务器解密手机号(节点 J-L)
- 5. 关联用户与生成登录态(节点 M)
- 6. 登录态存储与后续使用(节点 N-P)
- 四、具体代码实现
- 1.需求示例
- 2.代码实现(这里以页面上显示登录按钮为例)
- 五、关键注意事项
- 1.解密失败排查:
- 2.用户拒绝授权处理:
- 3.安全性要求:
一、小程序微信授权登录

1.登录态检查(节点 B)
- 操作方:小程序前端
- 目的:避免重复登录,提升用户体验
- 实现:
- 前端通过
wx.getStorageSync('token')检查本地是否存储了有效的自定义登录态(如Token)。 - 若有
Token,直接携带Token请求开发者服务器验证有效性,有效则进入主页; - 若无
Token或Token已过期,触发登录流程。
- 前端通过
2.获取临时凭证 code(节点 D)
- 操作方:小程序前端
- 核心接口:
wx.login() - 说明:
- 调用后微信客户端会向微信服务器请求,返回一个 一次性、有效期 5 分钟 的
code(临时登录凭证,仅能使用 1 次); - 此步骤 无需用户手动授权(静默获取,不弹授权弹窗)。
- 调用后微信客户端会向微信服务器请求,返回一个 一次性、有效期 5 分钟 的
文档直通车: 开放接口/登录/wx.login
3. 开发者服务器换用户标识(节点 F-G)
- 操作方:开发者服务器
- 核心接口:微信官方
auth.code2Session接口(GET 请求) - 请求参数(必须包含):
| 参数名 | 来源 | 说明 |
|---|---|---|
appid | 小程序管理后台获取 | 小程序唯一标识 |
secret | 小程序管理后台获取 | 小程序密钥(需保密,不可暴露在前端) |
js_code | 前端传递的 code | 临时登录凭证 |
grant_type | 固定值 | 填 "authorization_code" |
- 返回结果(微信服务器返回给开发者服务器):
openid:用户在当前小程序的 唯一标识(每个用户对每个小程序的openid不同);session_key:微信服务器与开发者服务器之间的 加密密钥(用于解密用户敏感信息,如手机号、头像昵称,需保密存储);- 若
code无效(如过期、重复使用),会返回错误码(如40029)。

文档直通车: 小程序登录/小程序登录
4. 生成自定义登录态(节点 H)
- 操作方:开发者服务器
- 核心目的:避免直接使用
openid/session_key传输,提升安全性 - 常见方案:
- 用
openid查询数据库,判断是否为新用户(新用户则创建用户记录,老用户则更新session_key); - 生成自定义登录态(如
JWT Token、随机字符串),并将Token与openid、session_key关联存储(如存入Redis、MySQL); Token需设置有效期(如 7 天),过期后需重新登录。
- 用
5.登录态存储与使用(节点 J-L)
- 前端存储:用
wx.setStorageSync('token', 开发者返回的 Token)保存到本地,下次打开小程序时可直接读取; - 后续请求:前端发起业务请求(如 “获取我的订单”)时,在请求头(如
Authorization: Bearer Token)或参数中携带Token; - 服务器验证:开发者服务器接收请求后,先验证
Token有效性(如检查Token是否存在、是否过期),验证通过则返回数据,否则返回 “登录失效”,前端需重新触发登录流程。
二、 需用户信息授权(如获取头像、昵称)

注意:微信已废弃旧的 wx.getUserInfo() 接口,当前需用 wx.getUserProfile() 且必须用户手动点击 “允许” 才能获取信息,不可静默授权。
文档直通车: 开放接口 /用户信息 /wx.getUserProfile
三、小程序获取手机号登录

1. 前置:获取临时登录凭证 code(节点 D)
- 操作方:小程序前端
- 核心接口:
wx.login() - 说明:与基础登录流程一致,静默获取 一次性、5 分钟有效期 的
code,用于后续换取session_key(解密手机号的关键密钥),此步骤无需用户授权。
2. 触发手机号授权(节点 E-F)

文档直通车: 表单组件 /button
- 操作方:小程序前端 + 用户
- 核心组件:需用小程序原生
button组件,且设置open-type="getPhoneNumber"(仅该类型按钮能触发手机号授权弹窗),示例代码:
<!-- WXML --><buttonopen-type="getPhoneNumber"bindgetphonenumber="handleGetPhoneNumber">用手机号登录
</button>
- 用户交互:点击按钮后,微信会弹出系统弹窗(“允许小程序获取你的手机号?”),用户需手动选择「允许」或「拒绝」,不可静默获取。
3. 获取加密手机号数据(节点 H)
- 操作方:小程序前端
- 触发事件:用户同意授权后,会触发
bindgetphonenumber事件,回调参数中包含加密的手机号信息:
// 前端 JS
handleGetPhoneNumber(e) {
if (e.detail.errMsg === "getPhoneNumber:ok") {
// 用户同意授权,获取加密数据
const encryptedData = e.detail.encryptedData; // 加密的手机号数据
const iv = e.detail.iv; // 解密所需的初始向量
// 后续将 encryptedData、iv 与 code 一起发送给服务器
} else {
// 用户拒绝授权,提示并返回
wx.showToast({ title: "需授权手机号才能登录", icon: "none" });
}
}
- 关键说明:
encryptedData和iv是加密后的字符串,前端无法解密,必须传给开发者服务器,用session_key解密。
4. 服务器解密手机号(节点 J-L)
- 操作方:开发者服务器
- 核心步骤:
- 换取
session_key:用前端传的code,调用微信auth.code2Session接口(参数含appid、appsecret、code),获取openid(用户唯一标识)和session_key(解密密钥); - 解密手机号:用
session_key、iv对encryptedData进行解密(需用微信官方推荐的加密库,如Node.js用crypto模块,Java用AES解密工具);
- 换取
- 解密结果:解密后会得到包含手机号的
JSON数据,示例:
{
"phoneNumber": "13800138000", // 用户手机号(核心信息)
"purePhoneNumber": "13800138000",
"countryCode": "86" // 国家码
}
5. 关联用户与生成登录态(节点 M)
- 操作方:开发者服务器
- 核心逻辑:
- 用户关联:用解密后的手机号(或
openid)查询数据库,判断是否为新用户:- 新用户:创建用户记录,存储手机号、
openid等信息; - 老用户:更新用户的
session_key(若已过期),关联已有账号;
- 新用户:创建用户记录,存储手机号、
- 生成登录态:创建自定义登录态(如
JWT Token、随机字符串),设置有效期(如 7 天),并将Token与用户信息关联存储(如Redis、MySQL);
- 用户关联:用解密后的手机号(或
- 安全注意:
session_key和手机号属于敏感信息,不可返回给前端,仅在服务器端存储和使用。
6. 登录态存储与后续使用(节点 N-P)
- 前端存储:接收服务器返回的
Token,用wx.setStorageSync('token', Token)保存到本地,下次打开小程序时直接读取; - 后续请求:发起业务请求(如 “查询我的订单”)时,在请求头(如
Authorization: Token xxx)携带Token; - 服务器验证:服务器接收请求后,验证
Token有效性(是否存在、是否过期),通过则返回数据,否则提示 “登录失效”,触发重新登录。
四、具体代码实现
1.需求示例
一般登录按钮会有两种方式出现:
单独的页面(项目logo、登录、取消)

一个公共弹框(从页面底部或中间弹出授权弹框)

2.代码实现(这里以页面上显示登录按钮为例)

<template><view class="demo-login-container"><button class="btn" open-type="getPhoneNumber" @getphonenumber="getphonenumber">手机号快捷登录</button><view class="black2 font28 mt28" @click="cancelLogin">取消登录</view></view></template><script lang="ts" setup>import Taro from '@tarojs/taro'import loginService from "../../server/login/loginService"// // 获取codeconst getCode = async () => {const { code } = await Taro.login()Taro.setStorageSync('code', code)}const getphonenumber = async(e) => {console.log("getphonenumber -------- e", e);console.log("pageObj.code", Taro.getStorageSync('code'));let { encryptedData, iv } = e.detail;console.log("encryptedData-------------------------", encryptedData, iv);if (!iv) return;try {let res;// 将code 、 encryptedData、iv 发送到服务器res = await loginService.login({ code: Taro.getStorageSync('code'), encryptedData, iv });console.log("res", res);// 登录成功处理Taro.setStorageSync('token', res.data.access_token);Taro.setStorageSync('userInfo', res.data.user_info);// 一些你的逻辑,比如页面跳转:// Taro.switchTab({// url: '/pages/index/index'// })} catch (error) {console.error('登录失败:', error);Taro.showToast({title: error.message || '登录失败',icon: 'none'});getCode()}};const cancelLogin = () => {Taro.showToast({title: '取消登录',icon: 'none'})}</script><style lang="scss">.demo-login-container {width: 100%;display: flex;flex-direction: column;align-items: center;.btn {width: 600rpx;height: 80rpx;background-color: #4ea311;color: #fff;font-size: 32rpx;border-radius: 40rpx;text-align: center;}}</style>
我的项目框架为Taro,Uniapp和小程序原生也适用,改为对应的api就行
五、关键注意事项
1.解密失败排查:
若解密失败,常见原因:session_key 过期(wx.login() 需重新调用获取新code换 session_key)、encryptedData/iv 传输错误、appid/appsecret 配置错误;
2.用户拒绝授权处理:
需友好提示 “授权手机号是登录必要条件”,并提供重新授权入口(不可强制拦截用户);
注意
小程序审核是要求,用户能取消登录的,所以大家在做登录时,不能强制用户登录
3.安全性要求:
appsecret 和 session_key 必须在服务器端存储,严禁暴露在前端代码中(防止被窃取后解密用户手机号);
建议对前端与服务器的通信采用 HTTPS 协议,避免数据传输被劫持。