token过期自动续期解决方案 (附带代码)

940 阅读2分钟
前端封装统一接口请求函数、token刷新函数,在请求成功之后对返回结果进行校验,如果token过期,则调用token刷新函数请求新的token.\
后端在接收到token刷新请求之后通过结合redis中存放的用户信息、token和refresh_token对请求参数进行验证,验证通过之后生成新的token和refresh_token存放到redis中并返回给前端。至此完成token刷新。

import ajax from '@/uni_modules/u-ajax/js_sdk'
import {
	baseURL,
	tokenName,
	tokenTableName,
	requestTimeout,
	successCode,
	contentType,
	invalidCode,
	decryptKey
} from '@/config/index.js'
import {
	decryptDes
} from '@/utils/encry/des.js'
import store from '@/store/index.js'

// 创建请求实例
const configuration = {
	// 初始配置
	baseURL: baseURL,
	timeout: requestTimeout,
	header: {
		'Content-Type': contentType
	}
}
const instance = ajax.create(configuration)
// 创建刷新token请求实例
const updateInstance = ajax.create({})
// 刷新请求对象
let ajaxData = {}
// 刷新锁
let refreshLock = false
// 缓存对象
let obj = {}
// 防抖
let stabilization = true
//PAN
// 添加请求拦截器
instance.interceptors.request.use(
	config => {
		//发请求前做的一些处理,数据转化,配置请求头,设置token,设置loading等,根据需求去添加
		let token = uni.getStorageSync(tokenTableName); //这里取token之前,你肯定需要先拿到token,存一下
		if (
			contentType === 'application/x-www-form-urlencoded;charset=UTF-8' &&
			config.data
		) {
			config.data = qs.stringify(config.data)
		}
		if (token) {
			token = JSON.parse(token)
			config.header[tokenName] = token.authToken;
		}
		return config
	},
	error => {
		uni.showToast({
			title: response.data.msg,
			icon: "none",
			duration: 2000
		});
		// 对请求错误做些什么
		return Promise.reject(error)
	}
)
// // 添加响应拦截器
instance.interceptors.response.use(
	response => {
		// 对响应数据做些什么
		if (successCode.includes(response.data.code)) {
			return response.data
		} else {
			if (!response.config.url.includes('upms/api/v1/auth/refresh')) {
				if (refreshLock) {
					return instance(
						obj[response.config.url.split(baseURL)[1]]
					)
				} else {
					setTimeout(() => {
						refreshLock = false
					}, 20)
				}
			}
		}
		if (invalidCode.includes(response.data.code) || response.data.code == 401) {
			uni.showToast({
				title: response.data.msg,
				icon: "none",
				duration: 2000
			});
			uni.setStorageSync(tokenTableName, '');
			if (stabilization) {
				stabilization = false
				store.commit("judgeToken", false);
				uni.navigateTo({
					url: '/pages/login/index'
				});
				setTimeout(() => {
					stabilization = true
				}, 5000)
			}
		} else {
			uni.showToast({
				title: response.data.msg,
				icon: "none",
				duration: 2000
			});
			return Promise.reject(response.data.msg)
		}
	},
	error => {
		// 对响应错误做些什么
		return Promise.reject(error)
	}
)
// 导出 create 创建后的实例
let subscribers = [];

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

function addSubscriber(callback) {
	subscribers.push(callback)
}
let isRefreshing = true;

function checkStatus(response) {
	if (rresponse == null) {
		// 刷新token的函数,这需要添加一个开关,防止重复请求
		let token = uni.getStorageSync(tokenTableName); //这里取token之前,你肯定需要先拿到token,存一下
		if (token) {
			token = JSON.parse(token)
			if (isRefreshing) {
				refreshTokenRequst()
			}
		}
		isRefreshing = false;
		// 这个Promise函数很关键
		const retryOriginalRequest = new Promise((resolve) => {
			addSubscriber(() => {
				resolve(request(url, options))
			})
		});
		return retryOriginalRequest;
	} else {
		return response;
	}
}

function refreshTokenRequst() {
	let config = {}
	config.method = "POST"
	config.url = baseURL + '/upms/api/v1/auth/refresh'

	let token = uni.getStorageSync(tokenTableName); //这里取token之前,你肯定需要先拿到token,存一下
	token = JSON.parse(token)
	config.header = {}
	config.header['R-Authorization'] = token.refreshToken;
	ajax(config).then((response) => {
		console.log(response)
		if (successCode.includes(response.data.code)) {
			uni.setStorageSync(tokenTableName, decryptDes(response.data.data, decryptKey));
		}
		isRefreshing = true;
		onAccessTokenFetched();
	});
}
export default function request(config) {
	let token = uni.getStorageSync(tokenTableName) || ''; //这里取token之前,你肯定需要先拿到token,存一下
	if (token) {
		token = JSON.parse(token)
		if (token.expiresAtTs < (new Date().getTime() + 5 * 60 * 1000)) {
			if (isRefreshing) {
				isRefreshing = false;
				refreshTokenRequst()
			}
			const retryOriginalRequest = new Promise((resolve) => {
				addSubscriber(() => {
					resolve(instance(config))
				})
			});
			return retryOriginalRequest;
		} else {
			return instance(config)
		}
	} else {
		return instance(config)
	}
}