为了保证系统安全性,用户一段时间不操作应该自动退出。在开发实现上,使用 token 过期来描述,请求到达网关时,若 token 已过期,则后端返回 401,前端自动清除 token。
之前版本为登录后2小时 token 自动过期,略有不妥,现在修改为用户2小时未操作 token 过期,若期间用户有操作,token 自动刷新。
之前实现
config/axios/service.ts
中通过后端返回参数判断,若收到401则自动清除 token,reload 本页面,这时触发permission.ts
中的路由守卫,判断无 token 状态后跳转 UMS 进行系统登录。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| service.interceptors.response.use( (response: AxiosResponse<any>) => { const { config, status, data } = response if (config.responseType === 'blob') { console.log('this is blob') return response } else if (status === result_code) { return data } else { ElMessage.error(data.message) } }, (error: AxiosError) => { console.log('error=', error) if (error && String(error).indexOf('401') > -1) { removeToken() window.location.reload() } else if (error && String(error).indexOf('403') > -1) { ElMessage.error('当前没有操作权限,请联系管理员。') } else if (error && String(error).indexOf('405') > -1) { removeToken() window.location.reload() } else { ElMessage.error(error.message) return Promise.reject(error) } } )
|
方法一:使用刷新 refresh token
登录成功后,后端会返回两个 token,一个是 access_token,一个是 refresh_token,分别保存在 cookie 中,后者的过期时间更长。若请求中 access_token 过期,则前端进行 refresh token 操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| const refreshToken = () => { const refreshToken = userStore.getToken('refresh_token') refreshToken({ params: refreshToken }) .then(data => { if (res.status === 'ok') { const accessToken = res.data.accessToken const refreshToken = res.data.refreshToken await userStore.updateToken(accessToken) await userStore.updateRefreshToken(refreshToken) } }) .catch(error => { console.error('刷新token失败:', error); }); }
|
缺点:refresh token操作是在用户请求之后进行的,可能会阻断用户请求,用户需要再次发起请求,没有实现无感刷新。
方法二:前端自动续期
使用 renewToken 函数定期检查 access_token 的过期时间
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| const renewToken() { const accessToken = userStore.getToken('accessToken'); const expireTime = userStore.getTime('expireTime'); const remainTime = expireTime - Date.now(); if (remainTime < 5 * 60 * 1000) { renewToken({ params: accessToken }) .then(data => { const accessToken = res.data.accessToken userStore.renewToken('expireTime', data.expireTime); }) .catch(error => { console.error('续期Token失败:', error); }); } }
setInterval(renewToken, 60 * 1000);
|
【注意】
- 需要同时延长 userInfo 和 AccessSyss 的过期时间,因为用户可能 focus 在子系统操作。
cookie.ts
中更新 | import Cookies from "js-cookie"
export const updateToken = (expireTime: string) => { const accessToken = Cookie.get(tokenKey) Cookies.remove(tokenKey) Cookies.set(tokenKey, token, { expires: expireTime }) const userInfo = Cookie.get(userKey) Cookies.remove(userKey) Cookies.set(userKey, JSON.stringify(userInfo), { expires: expireTime }) const sysList = Cookie.get(sysKey) Cookies.set(sysKey, sysList, { expires: expireTime }) Cookies.remove(sysKey) }
|
javascript