uniapp无痛刷新token继续网络请求
更新时间:2020-08-05 08:46
一.问题场景
用户请求后台用到两个token,一个是access_token,另一个是refresh_token.当access_token过期之后,需要使用refresh_token去后台请求新的access_token,然后拿着新的access_token继续后面的网络请求.
二.解决方案
二.一解决思路
1.本地过期时间校验;
2.请求token的请求用Promise形式包裹;
3.继续之前被打断的网络请求,将该网络请求修改成同步形式用到async和await,并在success方法中调用Promise中的resolve(res),将获取到的数据借助Promise返回;
二.二实现方案
1.用户成功登陆之后,将用户登录的时间和token过期时间记录下来,或者是记录后台返回的过期时间;
2.在封装的请求中,每次请求校验当前时间和过期时间之间的差值,如果"过期时间"-"当前时间"=时间差值>=10分钟(这个时间可以自己定义),则直接继续网络请求;如果时间差值<10分钟,则在发送该次网络请求之间先发送刷新access_token的请求,得到新的token再继续之前的网络请求;
3.发送刷新token的网络请求请使用Promise模式包裹,因为我个人封装的uniapp请求库是Promise模式的(参考链接:https://my.oschina.net/u/3943503/blog/4328562),需要返回一个Promise;
4.将正常的数据请求修改成同步模式用到async和await,然后调用该方法,在uni.request的success方法中调用resolve(res),将数据返回给页面上的请求,实现数据的正常接收和处理.
刷新的网络请求修改成同步,请参考这个博客:https://www.jianshu.com/p/8555a8451101
核心示例代码:
request(option) {
option.url = option.url || CONFIG.url;
option.header = option.header || CONFIG.header;
option.header["Authorization"] = uni.getStorageSync("token_type") + " " + uni.getStorageSync("access_token");
option.data = option.data || CONFIG.url;
option.method = option.method || CONFIG.method;
option.dataType = option.dataType || CONFIG.dataType;
let expiration = uni.getStorageSync('expiration');
// console.log();
// 时间格使用2020/10/10 12:00:00这种格式计算
let tmp = expiration.replace(/-/g, "/");
// console.log(tmp);
let futureTime = new Date(tmp).valueOf();//转换成毫秒
// console.log(futureTime);
let nowTime = new Date().valueOf();//转换成毫秒
// console.log(nowTime);
let jetLag = (futureTime - nowTime)/1000/60/60;
// let jetLag = (futureTime - nowTime) / 1000;
console.log(jetLag);
if (jetLag < 0.1) {
let appStr = "test:Hbgc_test@001",
wordArray = CryptoJS.enc.Utf8.parse(appStr),
appBase64 = CryptoJS.enc.Base64.stringify(wordArray),
header = {
"Authorization": "Basic " + appBase64,
"Content-type": "application/x-www-form-urlencoded"
},
data = {
"grant_type": "refresh_token",
"refresh_token": uni.getStorageSync("refresh_token")
};
return new Promise((resolve, reject) => {
uni.request({
url: Api.HOST + Api.LOGIN,
data: data,
header: header,
method: "POST",
success(res) {
// console.log(res2.statusCode);
if (res.statusCode == 401) {
uni.showToast({
icon: "none",
title: "用户未认证或登录过期,请重新登录"
});
setTimeout(function() {
uni.reLaunch({
url: "/pages/login/login"
});
}, 1000);
} else {
uni.setStorageSync("expiration", res.data.expiration);
uni.setStorageSync("expires_in", res.data.expires_in);
uni.setStorageSync("access_token", res.data.access_token);
uni.setStorageSync("id", res.data.id);
uni.setStorageSync("real_name", res.data.real_name);
uni.setStorageSync("refresh_token", res.data.refresh_token);
uni.setStorageSync("scope", res.data.scope);
uni.setStorageSync("token_type", res.data.token_type);
option.header = option.header || CONFIG.header;
option.header["Authorization"] = uni.getStorageSync("token_type") + " " +uni.getStorageSync("access_token");
requestIn();
async function requestIn(){
let [err2,res2] = await uni.request({
header: option.header,
url: option.url,
data: option.data,
method: option.method,
dataType: option.dataType,
});
uni.hideLoading();
if(res2.statusCode === 200){
resolve(res2);
}else{
uni.showToast({
icon:"none",
title:"状态码:"+res2.statusCode+"."+res2.data.message
});
return;
}
resolve(res2);
uni.showToast({
icon:"none",
title:"状态码:"+err2.statusCode+"."+err2.data.message
});
}
}
},
fail(err) {
reject(err);
console.log("err:" + JSON.stringify(err));
}
});
});
} else {
option.header = option.header || CONFIG.header;
option.header["Authorization"] = uni.getStorageSync("token_type") + " " + uni.getStorageSync("access_token");
return new Promise((resolve, reject) => {
uni.request({
header: option.header,
url: option.url,
data: option.data,
method: option.method,
dataType: option.dataType,
success(res) {
// console.log(JSON.stringify(res));
if(res.statusCode === 200){
resolve(res);
}else{
uni.showToast({
icon:"none",
title:"状态码:"+res.statusCode+"."+res.data.message
});
}
},
fail(err) {
uni.showToast({
icon:"none",
title:"状态码:"+err.statusCode+"."+err.data.message
});
reject(err);
}
});
});
}
},
说明:这里只是提供了思路和核心示例代码,仅供参考.
来源:oschina
链接:https://my.oschina.net/u/3943503/blog/4468430