小程序token失效问题

请求时token过期自动刷新token

  1. 在开发过程中,我们都会接触到token,token的作用是什么呢?了解一下Token 认证的来龙去脉主要的作用就是为了安全,用户登陆时,服务器会随机生成一个有时效性的token,用户的每一次请求都需要携带上token,证明其请求的合法性,服务器会验证token,只有通过验证才会返回请求结果。

  2. 当token失效时,现在的网站一般会做两种处理,一种是跳转到登陆页面让用户重新登陆获取新的token,另外一种就是当检测到请求失效时,网站自动去请求新的token,第二种方式在app保持登陆状态上面用得比较多。

  3. 下面进入主题,我们请求用的是axios,不管用何种请求方式,刷新token的原理都是一样的。

封装了一个统一的请求函数

import Vue from 'vue'
import {
	keepLogin
} from './publicFunction.js';
import api from './interface.js';
import {
	mapActions
} from 'vuex';
const commonUrl = "https://www.qixinlive.com:1443/"; //线上地址
// const commonUrl = "http://192.168.2.14:8010/"; //本地地址
// const commonUrl = "https://t.qixinlive.com:1441/"; //测试地址

const vue = new Vue()
let isRefreshing = true;
// post请求封装
function postRequest(url, data, type) {
	if (type == '1') {
		vue.$store.dispatch('loadingShowTrue')
	}
	var promise = new Promise((resolve, reject) => {
		var that = this;
		let newUrl = ''
		if (commonUrl === 'http://192.168.2.14:8010/') {
			let arr = url.split('/')
			newUrl = arr[0].substring(0, arr[0].length - 3) + '/' + arr[1]
		} else {
			newUrl = url
		}

		uni.request({
			url: commonUrl + newUrl,
			data: data,
			method: "POST",
			header: {
				"content-type": "application/json",
				token: uni.getStorageSync("token"),
			},
			success: (res) => {
				// 此判断可根据自己需要更改
				if (type == 'recognization') {
					resolve(res)

				} else if (type === 'confirm') {
					// reject(res);
					resolve(res)
				} else {
					if (type === 'confirm') {
						if (res.data.status !== 'success') {
							reject(res);
						}
					} else if (res.data.status !== 'success') {
						return uni.showToast({
							title: res.data.msg,
							icon: 'none'
						})
					}
					resolve(res)
				}
			},
			fail: (err) => {
				uni.showToast({
					title: '请求接口失败!',
					icon: 'error'
				})
				reject(err)
			},
			complete(res) {
				if (type == '1') {
					vue.$store.dispatch('loadingShowFalse')
					if (res.statusCode == 413) {
						uni.showToast({
							title: '上传文件过大',
							icon: 'error'
						})
					}
				}
				if (res.statusCode == 401) {
					if (isRefreshing) {
						refreshTokenRequst()
					}
					isRefreshing = false;
					const retryOriginalRequest = new Promise((resolve) => {
						addSubscriber(() => {
							resolve(postRequest(url, data))
						})
					});
					return retryOriginalRequest;
				} else {
					return res;
				}
			}
			//隐藏loading
		})
	});
	return promise

}


let subscribers = [];

function refreshTokenRequst() {
	keepLogin(api.userApi, uni.getStorageSync('userId'))
	setTimeout(() => {
		onAccessTokenFetched()
	}, 1000)
	isRefreshing = true;
}

function onAccessTokenFetched() {
	subscribers.forEach(callback => {
		callback()
	})
	subscribers = [];
}

function addSubscriber(callback) {
	subscribers.push(callback)
}

// get请求封装
function getRequest(url, data) {
	uni.showLoading({
		title: '加载中'
	})
	var promise = new Promise((resolve, reject) => {
		var that = this;
		var postData = data;
		uni.request({
			url: commonUrl + url,
			data: postData,
			method: "GET",
			dataType: "json",
			header: {
				"content-type": "application/json"
			},
			success: (res) => {
				// 此判断可根据自己需要更改
				if (res.data.status !== 'success') {
					return uni.showToast({
						title: '获取数据失败!'
					})
				}
				resolve(res)
			},
			fail: (err) => {
				uni.showToast({
					title: '请求接口失败!'
				})
				reject(err)
			},
			complete(res) {
				// 隐藏loading
				uni.hideLoading()

			}
		});
	});
	return promise;
}

export default {
	post: postRequest,
	get: getRequest,
};

总结:其实token失效,自动刷新token,在页面只有一个请求的时候是比较好处理的,但是如果页面同时有多个请求,并且都会产生token失效,这就需要一些稍微复杂的处理,解决方式主要是用了Promise 函数来进行处理。每一个token失效的请求都会存到一个Promise函数集合里面,当刷新token的函数执行完毕后,才会批量执行这些Promise函数,返回请求结果。还有一点要注意一下,这儿设置一个刷新token的开关isRefreshing,这个是非常有必要的,防止重复请求。

获取token

export function keepLogin(api, userId) {
	if (userId) {
		uni.request({
			url: `${api}driverRememberAutoLogin`,
			data: {
				userId: userId,
				// loginType: 11,
			},
			method: 'POST',
			success(reskeepLogin) {
				if (reskeepLogin.data.status === 'success') {
					if (reskeepLogin.data.driverTenantId) {
					    uni.setStorageSync('driverTenantId',reskeepLogin.data.driverTenantId)
					}
					uni.setStorageSync('userId',reskeepLogin.data.userId)
					uni.setStorageSync('token',reskeepLogin.data.accessToken)
					uni.setStorageSync('avatar',reskeepLogin.data.avatar)
					uni.setStorageSync('username',reskeepLogin.data.username)
					uni.setStorageSync('phone',reskeepLogin.data.secretMobilePhoneNumber)
				}
			},
			fail() {
				uni.showToast({
					title: '网络异常,稍后再试',
					icon: "none"
				})
			}
		})
	}
}
import Vue from 'vue'
const api = {

	//本地用接口
	driverWaybillApi: 'http://192.168.2.14:8010/driverWaybill/',
	orderCarrierApi: 'http://192.168.2.14:8010/ordercarrier/',
	driverAppCenterApi: 'http://192.168.2.14:8010/driverAppCenter/',
	driverpactApi: 'http://192.168.2.14:8010/driverpact/',
	receiptcarrierApi: 'http://192.168.2.14:8010/receiptcarrier/',
	userApi: 'http://192.168.2.14:8010/account/',
	carrierApi: 'http://192.168.2.14:8010/carrier/',
	userMobileApi: 'http://192.168.2.14:8010/msg/',
	//新增
	tenantApi: 'http://192.168.2.14:8010/tenant/',
	trackcarrierApi: 'http://192.168.2.14:8010/trackcarrier/',
	websocketApi: "ws://192.168.2.65:7200/",
	erCodeApi: 'http://192.168.2.14:8010/api/'
}

export default api
### 小程序登录失效的原因分析与解决方案 #### 1. **原因分析** 小程序登录失效的主要原因是 `session_key` 或者自定义的 `token` 超过了有效期限。微信并不会明确告知 `session_key` 的具体有效期,而是根据用户的活跃程度动态调整其生命周期[^1]。如果用户长时间未使用小程序,则可能导致 `session_key` 失效。 此外,在实际开发过程中,以下情况也可能导致登录失效: - 后端存储的 `session_key` 和 `openid` 数据被清除(如 Redis 缓存过期)。 - 用户主动退出登录或手动清理缓存。 - 前端未能及时更新本地存储中的 `token` 或 `session_id`。 - 微信服务器返回的新 `session_key` 未正确处理或覆盖旧值。 --- #### 2. **解决方案** ##### (1) 自动续期机制 为了应对 `session_key` 过期的情况,可以设计一套自动续期逻辑。当检测到当前会话已失效时,触发重新登录操作,并通过静默方式完成身份验证。以下是实现思路: - 在每次接口调用前,先校验本地存储的 `token` 是否有效。 - 如果无效,则调用微信 API 获取新的 `code` 并发送至后端换取最新的 `session_key` 和 `openid`。 - 更新后的数据需同步保存到客户端和服务器两端。 示例代码如下: ```javascript // uni-app 实现自动续期功能 function checkLogin() { const token = uni.getStorageSync('token'); if (!token || isTokenExpired(token)) { // 判断 token 是否过期 return renewSession(); } } async function renewSession() { try { const loginRes = await wx.login(); // 获取 code const res = await request('/login', { code: loginRes.code }); // 请求后端刷新 session uni.setStorageSync('token', res.token); // 更新 token return true; } catch (e) { console.error('Renew Session Failed:', e); return false; } } ``` 上述方法基于 UniApp 开发框架,适用于大多数场景下的登录状态管理[^3]。 --- ##### (2) 使用云函数简化流程 对于无需额外部署后端服务的小型项目,可借助微信提供的云开发能力来减少复杂度。例如利用 `uniCloud` 提供的云函数快速实现登录认证逻辑。 核心步骤包括: 1. 客户端调用 `wx.login()` 方法获取临时登录凭证 `code`; 2. 将该 `code` 发送至指定云函数; 3. 在云函数内部执行 `(https://api.weixin.qq.com/sns/jscode2session)` 接口请求,解析得到 `openid` 和 `session_key`; 4. 返回结果给前端用于初始化全局变量或其他业务需求。 注意:此方案依赖于腾讯云环境配置以及相关权限设置,请提前做好学前准备工作。 --- ##### (3) Token 验证优化策略 针对某些特殊情况下仍可能出现异常断连的现象,建议引入更健壮的身份识别手段——JWT(Json Web Tokens)[^4] 。相比传统 Cookie/Session 方案而言具备诸多优势特性比如无状态化易于扩展跨域支持良好等等优点非常适合移动端应用架构设计模式下采用。 简单来说就是每当成功登陆之后由服务器签发一个包含必要信息(如 user id , roles etc.)经过加密签名过的字符串形式令牌交给客户端保管;以后每一次发起网络请求的时候都将它附加进去作为参数传递过去让接收方能够据此判断访问者的合法性进而决定允许还是拒绝相应资源的操作行为发生与否即可达成目的效果啦! 下面给出一段伪代码展示如何生成及检验这样的安全标记对象实例演示过程仅供参考学习用途而已哦~ ```python import jwt from datetime import timedelta, datetime SECRET_KEY = 'your_secret' def create_token(user_info): payload = { 'exp': datetime.utcnow() + timedelta(days=7), 'iat': datetime.utcnow(), 'sub': user_info['openId'] } encoded_jwt = jwt.encode(payload, SECRET_KEY, algorithm='HS256') return encoded_jwt.decode() def verify_token(token): try: decoded = jwt.decode(token, SECRET_KEY, algorithms=['HS256']) return True, decoded['sub'] # Return OpenID on success except Exception as e: return False, str(e) ``` 以上 Python 版本展示了 JWT 创建与验证的基础原理 ,当然也可以移植成 JavaScript 形式应用于 Node.js 等运行环境中去满足不同平台间互操作性的需求呢~ --- #### 总结 综上所述,解决小程序登录失效的关键在于建立完善的会话管理和身份验证体系。无论是采取传统的 `session_key` 续期办法还是现代化的 JWT 技术路线都能有效地缓解此类问题带来的困扰。开发者应依据自身项目的实际情况灵活选用合适的技术路径加以实施改进工作才行呀! ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值