前一阵遇到了一个问题,线上平台有时会出现用户正在使用的时候,突然要用户去进行登录,这样会造成很不好的用户体验,但是当时一直没有好的思路因此搁置了下来;通过零散时间查询资料以及思考,最终解决了这个问题,接下来跟大家分享一下!
环境
- 请求采用的 Axios V1.3.2。
- 平台的采用的
JWT(JSON Web Tokens)
进行用户登录鉴权。
(拓展:JWT 是一种认证机制,让后台知道该请求是来自于受信的客户端;更详细的可以自行查询相关资料)
问题现象
线上用户在使用的时候,偶尔会出现突然跳转到登录页面,需要重新登录的现象。
原因
- 突然跳转到登录页面,是由于当前的 token 过期,导致请求失败;在
axios
的响应拦截axiosInstance.interceptors.response.use
中处理失败请求返回的状态码 401,此时得知token
失效,因此跳转到登录页面,让用户重新进行登录。 - 平台目前的逻辑是在
token
未过期内,用户登录平台可直接进入首页,无需进行登录操作;因此就存在该现象:用户打开平台,由于此时token
未过期,用户直接进入到了首页,进行其他操作。但是在用户操作的过程中,token
突然失效了,此时就会出现突然跳转到登录页面,严重影响用户的体验感!
(注:目前线上项目中存在数据大屏,一些实时数据的显示;因此存在用户长时间停留在大屏页面,不进行操作,查看实时数据的情况)
切入点
- 怎样及时的、在用户感知不到的情况下更新
token
? - 当
token
失效的情况下,出错的请求可能不仅只有一个;当失效的token
更新后,怎样将多个失败的请求,重新发送?
操作流程
好了!经过了一番分析后,我们找到了问题的所在,并且确定了切入点;那么接下来让我们实操,将问题解决掉。
前要:
1、我们仅从前端的角度去处理。
2、后端提供了两个重要的参数:accessToken
(用于请求头中,进行鉴权,存在有效期);refreshToken
(刷新令牌,用于更新过期的 accessToken,相对于 accessToken 而言,它的有效期更长)。
1、处理 axios 响应拦截
注:在我实际的项目中,accessToken
过期后端返回的 statusCode
值为 401,需要在axiosInstance.interceptors.response.use
的 error
回调中进行逻辑处理。
// 响应拦截 axiosInstance.interceptors.response.use( (response) => { return response; }, (error) => { let { data, config } = error.response; return new Promise((resolve, reject) => { /** * 判断当前请求失败 * 是否由 toekn 失效导致的 */ if (data.statusCode === 401) { /** * refreshToken 为封装的有关更新 token 的相关操作 */ refreshToken(() => { resolve(axiosInstance(config)); }); } else { reject(error.response); } }) } )
- 我们通过判断
statusCode
来确定,是否当前请求失败是由token
过期导致的; - 使用 Promise 处理将失败的请求,将由于
token
过期导致的失败请求存储起来(存储的是请求回调函数,resolve 状态)。理由:后续我们更新了token
后,可以将存储的失败请求重新发起,以此来达到用户无感的体验
2、封装 refreshToken 逻辑
要点:
- 存储由于
token
过期导致的失败的请求。 - 更新本地以及axios中头部的
token
。 - 当
refreshToken
刷新令牌也过期后,让用户重新登录。
// 存储由于 token 过期导致 失败的请求 let expiredRequestArr: any[] = []; /** * 存储当前因为 token 失效导致发送失败的请求 */ const saveErrorRequest = (expiredRequest: () => any) => { expiredRequestArr.push(expiredRequest); } // 避免频繁发送更新 let firstRequre = true; /** * 利用 refreshToken 更新当前使用的 token */ const updateTokenByRefreshToken = () => { firstRequre = false; axiosInstance.post( '更新 token 的请求', ).then(res => { let { refreshToken, accessToken } = res.data; // 更新本地的token localStorage.setItem('accessToken', accessToken); // 更新请求头中的 token setAxiosHeader(accessToken); localStorage.setItem('refreshToken', refreshToken); /** * 当获取了最新的 refreshToken, accessToken 后 * 重新发起之前失败的请求 */ expiredRequestArr.forEach(request => { request(); }) expiredRequestArr = []; }).catch(err => { console.log('刷新 token 失败err', err); /** * 此时 refreshToken 也已经失效了 * 返回登录页,让用户重新进行登录操作 */ window.location.href = `${HOME_PAGE}/login`; }) } /** * 更新当前已过期的 token * @param expiredRequest 回调函数,返回由token过期导致失败的请求 */ export const refreshToken = (expiredRequest: () => any) => { saveErrorRequest(expiredRequest); if (firstRequre) { updateTokenByRefreshToken(); } }
总结
经过一波分析以及操作,我们最终实现了实际项目中的无感刷新token
,最主要的是有效避免了:用户在平台操作过程中突然要退出登录的现象(尤其是当用户进行信息填写,突然要重新登录,之前填写的信息全部作废,是很容易让人发狂的)。
到此这篇关于js项目中前端如何实现无感刷新token的文章就介绍到这了,更多相关js 无感刷新token内容请搜索IT俱乐部以前的文章或继续浏览下面的相关文章希望大家以后多多支持IT俱乐部!