这里写自定义目录标题
请求时token过期自动刷新token
-
在开发过程中,我们都会接触到token,token的作用是什么呢?了解一下Token 认证的来龙去脉主要的作用就是为了安全,用户登陆时,服务器会随机生成一个有时效性的token,用户的每一次请求都需要携带上token,证明其请求的合法性,服务器会验证token,只有通过验证才会返回请求结果。
-
当token失效时,现在的网站一般会做两种处理,一种是跳转到登陆页面让用户重新登陆获取新的token,另外一种就是当检测到请求失效时,网站自动去请求新的token,第二种方式在app保持登陆状态上面用得比较多。
-
下面进入主题,我们请求用的是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