1.背景
最近开发了一个微信小程序,但是后端这里想简化处理,没有严格给出token的自动刷新接口,同时对于token的有效期时间也没有配置。前端这里觉得既然要简化就简化处理吧,于是就利用vuex缓存,后来在实际使用的过程中发现了一些问题,这里总结一下。
2.问题一:token的刷新时机?
最开始设置的是不刷新模式,用户首次进入,判断是否登录(isLogin),这里isLogin与token是否存在直接绑定,如果没有token意味着isLogin为false,这时在调用后端接口的时候借助request拦截器来拦截token失效状态码(B0003),如果成功拦截到这个token失效码,则主动帮助用户跳转到登录模块(即这里的toLogin()
):
function baseRequest(url, method, data, {
noAuth = false
}) {
let Url = HTTP_REQUEST_URL,
header = HEADER;
if (!noAuth) {
if (!store.state.app.token && !checkLogin()) {
toLogin();
return Promise.reject({
msg: i18n.t(`未登录`)
});
}
}
if (store.state.app.token) header[TOKENNAME] = store.state.app.token;
return new Promise((resolve, reject) => {
uni.request({
url: Url + "/" + url,
method: method || 'GET',
header: header,
data: data || {},
success: (res) => {
if (res.data.code === "00000") {
resolve(res.data.data);
} else if (res.data.code === "B0003") {
reject({
msg: i18n.t(`token失效,请重新登录~`)
});
setTimeout(() => {
toLogin();
}, 1500);
} else {
reject({
msg: i18n.t(res.data.msg || `系统错误~`)
});
}
},
fail: (msg) => {
reject({
msg: i18n.t(msg || "系统错误~")
});
}
})
});
}
这样存在什么问题呢?乍一看好像没有问题,但是站在用户的角度,用户可能前几天使用了小程序,结果今天使用的时候进去发现token失效,请重新登录~
,给用户的感觉不是很友好,于是决定采用一层静默登录。实现方式是,用户进入小程序首页,在生命周期函数Onload里面调用登录接口,这样能够保证信息同步,但是实际使用发现,如果浏览的小程序页面较多的话,那么onLoad不止调用一次,相当于给了一个token频繁刷新,这样显然也不太合理。
如果是使用缓存呢,首次进入之后刷token同时在缓存中记录这个token已经被刷新过(isTokenRefresh
),可以是可以,但是还存在一个小问题,如果token失效了,需要让用户跳转登录模块,走一遍登录授权,这时将isTokenRefresh
设置为false,那么登录完成之后,回到首页会导致token因为登录流程刷了一次,回到首页又刷了一次。那可以不修改isTokenRefresh
不,这样是不行的,那这样在之后的登陆中变成了单次可用了。要是不走登录流程呢,token失效了直接isTokenRefresh
设置为false,回到首页不行吗,这样也不太好,因为可能用户主动退出登录,让用户重新走登录流程更合理。因此,缓存方案不是特别好。
于是问了一下豆包,豆包这里提到了在原型中挂全局变量,这种方案是可行的:
还有一种类似的方案,借助vuex不能够在应用中持续缓存的特点,首次初始化isTokenRefresh
为false,然后在vuex中调用UPDATE_TOKEN标记token被刷新过,这样也能够很好的解决首次进入单次刷新token问题了。
//pages/index/index
onLoad() {
const isTokenRefresh = this.$store.state.app.isTokenRefresh;
if (!isTokenRefresh) {
Routine.getCode().then(code => {
authType({ code }).then(data => {
const {userAvatar, userNick,token} = data;
this.$store.commit('UPDATE_USERINFO', { nickname: userNick, avatar: userAvatar});
this.$store.commit('LOGIN', {
token: token
});
this.$store.commit('UPDATE_TOKEN');
this.initData();
});
});
} else {
this.initData();
}
},
//vuex
const state = {
token: uni.getStorageSync('LOGIN_STATUS') || false,
userInfo: uni.getStorageSync('USER_INFO') ? JSON.parse(uni.getStorageSync('USER_INFO')) : {},
homeActive: false,
isTokenRefresh: false,
};
const mutations = {
UPDATE_TOKEN(state) {
state.isTokenRefresh = true;
},
}
3.如果后端可以无限配合,token该怎么维护呢?
下面给出最理想的方案,后端能够按照我们前端所想无限配合,那这块该如何开发呢?那这里就需要准备两个接口了,一个是token是否有效isTokenValid
,还有一个token刷新接口是refreshToken
,用户进入首页首先调用isTokenValid
检测token是否有效,如果失效直接调用refreshToken
刷一次,否则不需要刷新,这样能够保证token的使用时间最大化;如果token失效了,则主动调用一次refreshToken
刷新,对于用户主动退出登录状态的情况,可以设置一个manualLogout
字段来配合,在刷refreshToken
刷新之前判断一下是否是主动退出,如果主动退出那么重置manualLogout
为false并走登录流程。类似的还可以后端给一个token的失效时间,但是这样在账号token被顶这个失效时间会异常,最好的还是后端去判断是否有效。最后,很感谢您能够读到这里,希望对您有所帮助,关注点赞分享更多前端知识。