关于Token过期的问题

16 篇文章 0 订阅

access_token

作用:获取需要授权的接口数据

expires_in

作用:access_token 过期的时间

refresh_token

作用:刷新获取新的 access_token

为什么access_token需要有过期时间以及比较短

为了安全

怎么处理?

方法一:

在请求发起拦截每个请求,判断token的有效时间是否已经过期,若已过期,则讲请求挂起,先刷新token后再继续请求

  • 优点:在请求前拦截,能节省请求,省流量

  • 缺点:需要后端额外提供一个token过期时间的字段;使用了本地时间判断,若本地时间被篡改,特别是本地时间比服务器时间慢时,拦截会失败

 

方法二:

不在请求前拦截,而是拦截返回后的数据。先发起请求,接口返回过期后,先刷新token,再进行一次重试

  • 优点:不需要额外的token过期字段,不需判断时间

  • 缺点:会消耗多一次请求,耗流量

 

综上所述,方法一和方法二优缺点是互补的,方法一有校验失败的风险(本地时间被篡改时),方法二更简单粗暴,等知道服务器已经过期了再重试一次,只是消耗多一个请求

代码案例

import axios from 'axios'
import store from '@/store'
import { Message } from 'element-ui'
import router from '@/router'
import qs from 'qs'

const request = axios.create({
  // 配置选项
  // baseURL,
  // timeout
})

function redirectLogin () {
  router.push({
    name: 'login',
    query: {
      redirect: router.currentRoute.fullPath
    }
  })
}

function refreshToken () {
  return axios.create()({
    method: 'POST',
    url: '/front/user/refresh_token',
    data: qs.stringify({
      // refresh_token 只能使用1次
      refreshtoken: store.state.user.refresh_token
    })
  })
}
// 请求拦截器
// Add a request interceptor
request.interceptors.request.use(function (config) {
  // console.log('接口请求进来了', config)
  // 通过改写 config 配置信息来实现业务逻辑功能的统一处理
  const { user } = store.state
  if (user && user.access_token) {
    config.headers.Authorization = user.access_token
  }
  // 注意:这里一定要返回 config,发欧洲请求就发布出去
  return config;
}, function (error) {
  // Do something with request error
  return Promise.reject(error);
});

// 响应拦截器
let isRfreshing = false // 控制刷新 token 状态
let requests: any[] = [] // 储存刷新 token 期间过来的401请求
request.interceptors.response.use(function (response) {
  // 状态码为2xx 进入这里
  // console.log('请求响应成功了 => ', response)
  // 如果是自定义错误状态,错误处理就写到这里
  return response
}, async function (error) {
  if (error.response) {
    // 请求收到响应了,但是状态超出了 2xx 的范围
    const { status } = error.response
    if (status === 400) {
      Message.error('请求参数错误')
    } else if (status === 401) {
      // token无效
      if (!store.state.user) {
        redirectLogin()
        return Promise.reject(error)
      }

      // 刷新token
      if (!isRfreshing) {
        isRfreshing = true // 开启刷新状态
        // 尝试刷新获取新的token
        return refreshToken().then(res => {
          if (!res.data.success) {
            throw new Error('刷新 Token 失败')
          }
          // 刷新token 成功了
          store.commit('setUser', res.data.content)
          // 把requests 队列中的请求重新发送出去
          return request(error.config)
          requests.forEach(cb => cb());
          // 重置 request数组
          requests = []
        }).catch(err => {
          console.log(err)
          store.commit('setUser', null)
          redirectLogin()
          return Promise.reject(error)
        }).finally(() => {
          isRfreshing = false
        })
      }
      // 刷新状态下,把请求挂起放到 requests 数组中
      return new Promise(resolve => {
        requests.push(() => {
          resolve(request(error.config))
        })
      })
    } else if (status === 403) {
      Message.error('没有权限,请联系管理员')
    } else if (status === 404) {
      Message.error('请求资源不存在')
    } else if (status >= 500) {
      Message.error('服务端错误,请联系管理员')
    }
  } else if (error.request) {
    // 请求发出去没有收到响应
    // console.log(error.request);
    Message.error('请求超时,请刷新重试')
  } else {
    // 在设置请求时发生的错误
    // console.log('Error', error.message);
    Message.error(`请求失败:${error.message}`)
  }
  console.log(error.config);

  // 把请求失败的错误对象继续抛出,扔给下一个上一个调用者
  return Promise.reject(error);
})

export default request

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值