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

  • 6
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值