我们为什么要无感刷新呢?
我们都知道,后台返回的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