十堰小程序开发|微信小程序登录鉴权全流程:wx.login、code2Session、手机号一键登录与Token体系设计
为什么登录鉴权是小程序开发的第一道坎
很多十堰企业在做小程序时,把注意力全放在页面设计和功能上,直到上线才发现:用户换个手机就"丢了账号",后台数据对不上人,营销活动被羊毛党刷爆。这些问题的根源,几乎都出在登录鉴权体系没有设计好。小程序的登录方式和传统 Web 完全不同——它没有 Cookie 会话,用户身份依托微信生态的 openid,一套设计不当的鉴权方案,后期重构成本极高。本文把微信小程序登录鉴权的完整链路讲透,并给出可直接落地的代码与 Token 设计思路。
一、微信登录的三步底层流程
1.1 第一步:wx.login 拿临时 code
小程序端调用 wx.login(),微信会返回一个临时登录凭证 code。这个 code 有效期只有 5 分钟,且只能使用一次。注意:code 绝对不能在前端直接换取 openid,因为换取需要用到 AppSecret,而 AppSecret 一旦泄漏,别人就能冒用你的小程序身份,伪造登录。
wx.login({
success(res) {
// 把 res.code 发给自己业务后端,而不是直接请求微信
wx.request({
url: 'https://api.example.com/login',
method: 'POST',
data: { code: res.code }
})
}
})
1.2 第二步:服务端 code2Session 换 openid
后端收到 code 后,调用微信的 code2Session 接口:
GET https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=CODE&grant_type=authorization_code
返回内容包括 openid、session_key 以及可选的 unionid。openid 是用户在当前小程序内的唯一标识,你应把它存进自己的 users 表,作为识别用户的依据。整个换取过程必须在服务器完成,前端只负责把 code 转发过来。
1.3 openid 与 unionid 的区别(关键)
openid 是"小程序级"的唯一 ID:同一个用户,在你的小程序 A 里是一个 openid,在你的小程序 B 里又是另一个 openid。而 unionid 是"微信开放平台账号级"的唯一 ID——只要多个小程序/公众号绑定在同一个开放平台账号下,同一用户的 unionid 就完全一致。十堰不少企业同时运营公众号和多个小程序,如果不做 unionid 打通,就会出现同一个人被当成多个账号,积分、会员等级、优惠券无法共享。建议:项目初期就把小程序绑定到微信开放平台,用 unionid 做主账号标识,把 openid 当作渠道标识来存。
二、手机号一键登录:更适合商业场景
很多业务(下单、预约、会员)需要真实手机号。微信开放了 getPhoneNumber 能力,用户点一下按钮即可完成手机号授权,无需短信验证码。流程是:前端通过 <button open-type="getPhoneNumber"> 拿到动态令牌 code,后端再调用 phonenumber.getPhoneNumber 接口换取手机号。
实测数据:相比传统的"短信验证码"流程,一键登录能把注册转化率提升约 30% 到 50%,同时省下每条 0.03 到 0.05 元的短信成本。对十堰本地做生活服务、门店预约类小程序的企业来说,这是性价比最高的登录方式。但要牢记:前端传回来的手机号永远不可信,必须以服务端换取的结果为准。
三、自建 Token 会话体系
3.1 为什么不能只靠 session_key
有人图省事,想把 session_key 直接当登录凭证发给前端,这是错误的:session_key 是用于解密用户敏感数据(如加密手机号)的密钥,一旦泄漏,用户的隐私数据就不保。正确做法是——服务端用 openid/unionid 建立自己的会话,签发一个自定义 Token 给前端,session_key 始终留在服务端。
3.2 JWT 还是服务端 Session
两种主流方案对比如下:
- JWT(无状态):服务端不存会话,适合多机部署与横向扩展;缺点是签发后无法立即失效,需要配合黑名单或短过期时间来兜底。
- 服务端 Session(Redis):可随时踢人下线、精确控制登录态;缺点是需要额外维护 Redis,多一层依赖。
落地建议:面向 C 端、以浏览为主的小程序用 JWT(accessToken 有效期 2 小时,refreshToken 有效期 30 天);涉及资金、权限敏感的后台则用 Redis Session,方便随时强制下线。
3.3 Token 刷新与过期处理
把 accessToken 设为短过期(如 2 小时),refreshToken 设为长过期。前端在请求拦截器里检测到 401,就自动用 refreshToken 换一个新的 accessToken,用户全程无感。注意 refreshToken 必须服务端可校验、可吊销,并绑定设备或版本号,防止被盗用长期冒名登录。
四、完整登录时序(前后端配合示例)
// 后端 Node.js 伪代码
app.post('/login', async (req, res) => {
const { code } = req.body;
const wxRes = await fetch(
`https://api.weixin.qq.com/sns/jscode2session?appid=${APPID}` +
`&secret=${SECRET}&js_code=${code}&grant_type=authorization_code`);
const { openid, unionid } = await wxRes.json();
const user = await User.findOrCreate({ where: { unionid }, defaults: { openid } });
const token = jwt.sign({ uid: user.id }, JWT_SECRET, { expiresIn: '2h' });
res.json({ token, uid: user.id });
});
前端拿到 token 后,用 wx.setStorageSync('token', token) 缓存,之后每个请求通过 header 携带 Authorization: Bearer <token>。服务端用统一中间件校验 token,校验失败返回 401,前端据此触发刷新或跳转登录。
五、必须避开的五个安全陷阱
- AppSecret 写进前端代码或提交到 Git 仓库 —— 必须放在服务端环境变量里。
- 用手机号明文当用户主键 —— 应加密存储,用内部自增 user_id 关联业务数据。
- Token 使用固定密钥且永不过期 —— 应支持密钥轮换,accessToken 短过期。
- 服务端不校验 code 是否已被使用 —— code 只能用一次,必须拦截重放请求。
- getPhoneNumber 的 code 不校验 —— 必须服务端换手机号,前端传的手机号不可信。
六、给十堰企业的落地建议
第一,登录流程应当是"先注册后授权":用户第一次进入就让系统用 openid 静默建立账号,需要手机号时再引导授权,避免一上来就弹窗把用户吓跑。第二,十堰小程序开发团队应把鉴权逻辑封装成统一中间件,让所有业务接口复用,避免每个接口各写一套、漏洞百出。第三,把 unionid 打通写进项目初期的技术方案,后期补做的代价非常大。第四,定期审计 Token 有效期与密钥轮换策略,配合日志监控异常登录行为,发现异常及时告警。
登录鉴权看起来只是一个小功能,但它决定了用户数据能不能沉淀、营销活动能不能防刷、后续功能能不能顺利扩展。把这套体系在设计阶段做对,远比上线后到处打补丁要省下数倍的开发与运维成本。对于正在规划小程序的十堰企业来说,先把登录鉴权的地基打牢,后面的每一层楼都会更稳。
