每天几个面试题|token过期?页面如何实现无感刷新?

我们为什么要无感刷新呢?
我们都知道,后台返回的token是有时效性的,时间到了,你在交互后台的时候,后台会判断你的token是否过期(安全需要),如果过期了就会通过邪恶的手段逼迫你重新登陆!
【每天几个面试题|token过期?页面如何实现无感刷新?】
无感刷新是什么?
前面说到缩短token时间就会加大我们网站的安全性,但是缩短token用户就会生气了,所以为了让用户不生气,我们使用双token刷新、续期。
双token是啥?access_token(短token—请求时带给后台的)和refresh_token(长token—用于刷新)
活跃用户是啥? 我们这里判定access_token创建开始到2*access_token的时间为活跃(只要在这个时间段内,监听用户有操作就是活跃).

使用双token
用户第一次用账号密码登录,服务器返回3个参数: access_token、refresh_token和expires_in(短token过期时间,这里返回7200),时效长短不一样。短的access_token 时效过了之后,发送时效长的 refresh_token 重新获取一个短时效token,如果都过期,就需要重新登录了。
refresh_token 就是用来刷新access_token 。
活跃用户的access_token 过期了,用refresh_token获取新的access_token。

步骤如下:
1-token过期根据refresh_token获取新的token 重新获取数据
2-创建一个新的axios实例 【使用request防止再次进入请求拦截和请求响应而进入死循环】
3-根据请求相应的响应值 是不是401 是:说明token过期
然后进行判断store中的 user :{token:'*****',refresh_token:'******'}中的 refresh_token和user对象是否存在 ,如果不存在说明之前没有登录过,直接去登录
4-使用新创建的axios 实例对象 requestFreshToken 发送新的请求 headers中的口令携带的是 refresh_token
5-获取token之后 将值重新赋值给user中的token
6-将user重新存入store中
7-重新获取刚才因为token失效而没有获取的数据 直接使用request 参数 来自error对象中【这里保存了之前token失效的请求数据】

代码如下:

// 导入axios, 后期会使用它创建出一个request实例, 用于发送请求 import axios from "axios"// 导入store, 后期会使用它对vuex里面的user信息进行修改操作 import store from "@/store"// 导入router, 后期会使用它进行路由跳转, 如果没有token和token过期直接跳转到login页面 import router from "@/router"// jsonBig是一个包, 主要是对后端返回大数字的问题进行处理 import jsonBig from "json-bigint"// 应该是移动端, 类似于element ui里面的this.$message.success等等 import { Toast } from "vant"// 第一个axios实例是用于发送请求 const request = axios.create({ // axios提供了一个api, transformResponse // 它可以将后端的原数据进行自定义操作 // 如果后端不进行处理, 那我们就可以使用下包的方式来进行手动的处理 transformResponse: [ function(data) { try { // 如果请求发送成功, 就将可能包含大数字的数据转换成一个BigNumber类型的对象, 它可以超出js的安全整数范围 return jsonBig.parse(data) } catch (err) { // 如果转换失败,则包装为统一数据格式并返回 return { data }; } } ] }); // 第二个axios实例用来当token失效的时候, refreshToken发送请求, 获取最新的token, 然后将错误的信息从新发送 // 作用就是防止, 再次使用第一个axios实例发送请求, 请求拦截器死循环 const requestFreshToken = axios.create()// 添加请求拦截器 request.interceptors.request.use( // 成功的回调 function(config) { // 如果user对象里面有token, 就说明是登录过的且token在失效期限内 if (store.state.user) { // Bearer 是一个http身份验证的规则, 发送请求必须加上 config.headers.Authorization = "Bearer " + store.state.user.token } return config },// 错误的回调 function(error) { return Promise.reject(error) } )// 添加响应拦截器 request.interceptors.response.use( // 响应拦截器的第一个回调函数, 成功走 function(response) { console.log(response, 3)return response; }, // 响应拦截器的第二个回到函数, 失败走 async function(error) { console.log(error.response, 222) // 获取错误的response中的status状态 const status = error.response.status// 如果浏览器返回的状态是为400, 那么就表示前端参数可能出现问题 if (status == 400) { // 使用vant中的toast方法抛出一个提示 Toast.file("请求参数错误") } else if (status == 401) { // 如果后端返回的状态为401, 就表示用户可能没有token, 或者token已经过期// 这里一共会出现三个情况: // 1. 用户没有登录 // 2. 用户有登录, 发送请求发现token已经过去 // 3. 出现异常// 调用vuex中state中存储的user信息 const { user } = store.state; // 这里走的是没有登录的情况 // 查看有没有存储refresh_token if (!user || !user.refresh_token) {// 如果没有就直接调换到login页面 return router.push("/login") }// 这里走的是token已经失效的情况 // 为什么需要try一下, 因为发送ajax可能会成功, 也可能会失败 try { // 这里使用的requestFreshToken实例发送请求异步请求, 因为如果在使用request会陷入死循环 const { data } = await requestFreshToken({ method: "PUT", url: "/v1_0/authorizations", headers: { // 直接使用登录时, 存储的refresh_token发送新请求, 获取新的token// 无感刷新也就在于这里 // 当浏览器发送请求, 请求拦截器发现token已经失效 // 那么就需要使用到refresh_token来发送请求, 获取新的token // 服务器响应之后将新的token存储本地, 最后将失败的请求发送出去 Authorization: "Bearer " + user.refresh_token } }); // 将服务器返回的新token重新保存到user里面 user.token = data.data.token// 注意, 想要修改state里面的数据, 必须通过mutations, 因为数据追踪的就知道是谁修改的 store.commit("setUser", user)return request(error.response.config) } catch (error) {}// 出现异常直接返回登录页 return router.push("/login") Toast.file("用户认证失败") } else if (status == 403) { // 客户端没有权限 Toast.file("客户端没有权限") } else if (status == 405) { //请求方法不支持 Toast.file("请求方法不支持") }// 如果服务器返回的状态码, 以上if判断都不存在, 那么直接抛出错误 return Promise.reject(error) } )// 最后导出request实例, 供其他组件发送ajax时使用 export default request


    推荐阅读