微信小程序登录鉴权实战:从核心原理到高可用架构设计
每次启动一个微信小程序项目,登录鉴权这个环节总是绕不过去。表面上看,不就是调用个 wx.login,后端换个 openid,然后发个 token 吗?但真到了线上环境,用户量一上来,各种稀奇古怪的问题就冒出来了:code 被重复使用导致 session_key 混乱、token 突然失效用户被踢出、网络抖动时登录态丢失……这些问题不解决,用户体验直接跌到谷底。
今天我们不聊那些文档里都有的基础步骤,而是深入实战中那些真正让人头疼的“坑”,以及如何构建一套既安全又健壮的登录鉴权体系。无论你是刚接手一个老项目,还是正在从零设计新系统,这里分享的思路和代码,或许能帮你少走不少弯路。
1. 重新理解登录流程:不止于 wx.login
很多开发者容易把小程序登录和获取用户信息混为一谈,这其实是两个独立但又紧密关联的环节。wx.login 的目标是静默获取用户的唯一身份标识(openid),而 wx.getUserProfile(注意,已不是旧的 getUserInfo)则是需要用户主动授权才能获取头像、昵称等公开信息。混淆两者,要么导致不必要的授权弹窗打扰用户,要么在需要用户信息时才发现根本没权限。
1.1 静默登录:构建用户身份基石
静默登录的核心在于无感。用户打开小程序,在毫无察觉的情况下,我们已经完成了一次身份握手。这个过程必须快速、可靠。
// 一个健壮的静默登录封装示例
class SilentLogin {
constructor() {
this.loginLock = false; // 防止并发重复登录
this.loginPromise = null; // 缓存登录Promise,避免重复请求
}
async execute() {
// 如果已有正在进行的登录请求,直接返回其Promise
if (this.loginPromise) {
return this.loginPromise;
}
// 检查本地是否有未过期的token
const localToken = wx.getStorageSync('auth_token');
const tokenExpire = wx.getStorageSync('token_expire');
if (localToken && tokenExpire && Date.now() < tokenExpire) {
// 本地token有效,直接使用
return Promise.resolve({ token: localToken, fromCache: true });
}
// 加锁,防止并发
if (this.loginLock) {
return new Promise(resolve => {
const checkInterval = setInterval(() => {
if (!this.loginLock) {
clearInterval(checkInterval);
this.execute().then(resolve);
}
}, 50);
});
}
this.loginLock = true;
this.loginPromise = new Promise(async (resolve, reject) => {
try {
// 1. 获取code
const loginRes = await wx.login();
if (!loginRes.code) {
throw new Error(`wx.login失败: ${loginRes.errMsg}`);
}
// 2. 请求服务端换取token
const serverRes = await wx.request({
url: 'https://your-api.com/auth/login',
method: 'POST',
data: { code: loginRes.code },
timeout: 10000 // 设置超时
});
if (serverRes.statusCode !== 200 || serverRes.data.code !== 0) {
throw new Error('服务端登录失败');
}
const { token, expire_in } = serverRes.data.data;
// 3. 安全存储
wx.setStorageSync('auth_token', token);
// 设置一个略短于实际过期时间的本地过期时间,预留缓冲
wx.setStorageSync('token_expire', Date.now() + (expire_in - 300) * 1000);
resolve({ token, fromCache: false });
} catch (error) {
reject(error);
} finally {
this.loginLock = false;
this.loginPromise = null;
}
});
return this.loginPromise;
}
}
// 全局单例
export const silentLogin = new SilentLogin();
这个封装解决了几个常见问题:
- 并发控制:防止短时间内多次调用
wx.login。 - 本地缓存:有效期内直接使用本地
token,减少网络请求。 - 错误处理:对每一步都进行健壮的错误捕获和提示。
1.2 session_key 的管理与安全边界
wx.login 成功后,微信服务器会返回一个 session_key。这个密钥非常敏感,因为它可以用于解密微信的加密数据(如获取手机号)。绝对不要把它传到前端!
安全提示:
session_key应仅存在于你的服务器内存或安全的缓存中(如Redis),并且需要设置合理的过期时间(与微信的session_key有效期同步,通常为24小时)。任何将session_key泄露给前端的做法都会引入严重的安全风险。


1029

被折叠的 条评论
为什么被折叠?



