vue axios token刷新--vue后台管理

4 篇文章 0 订阅

需求

最近在写项目,跟后端 讨论token刷新方案:前端登录后,后端返回token和refreshToken有效时间,当token过期时要求用refreshToken去获取新的token,前端需要做到无痛刷新token,即请求刷新token时要做到用户无感知。

分析

前端登录后会返回 tokenrefreshTokentoken30分钟过期 refreshToken一天过期,利用这个时间差就可以做到无痛刷新token

方法一:发起请求后拦截 当token 过期后调用refreshToken 接口 拿到新token 然后在吧之前的请求在执行一遍。当refreshToken过期,直接跳回登录页

方法二: 后端提供一个token过期时间的字段,在请求发起前拦截每个请求,判断token的有效时间是否已经过期,若已过期,则将请求挂起,先刷新token后再继续请求。

实现

这里我们实现方法一
这里需要跟后端约定好返回数据,比如:

{code: 418, message: 'token过期', data: {}}

import axios from "axios";

const instance = axios.create({
  baseURL: process.env.BASE_API, // url = base url + request url

  // timeout: 5000 // request timeout
  withCredentials: true,
 
});

// 给实例添加一个setToken方法,用于登录后将最新token动态添加到header,同时将token保存在localStorage中
instance.setToken = (token,refreshToken) => {
instance.defaults.headers['Authorization'] = `Auth ${token}`
// 这里用到的存储是localStorage
window.localStorage.setItem('token', token)
  
window.localStorage.setItem('refreshToken', refreshToken)
}

function refreshToken () {

  // 我项目中  更新token 需要吧原有的token 换成refreshToken去请求   这里根据需求可以改动
  window.localStorage.setItem('token', window.localStorage.refreshToken)
  return instance({method:'post',url: '/api-token-refresh'})
}
  // 设置了路由拦截  这里清掉token 就会跳回登录页
function removeToken () {
  window.localStorage.removeItem('token')
  window.localStorage.removeItem('refreshToken')

}

// 请求拦截器
instance.interceptors.request.use(
  config => {
    // 每次发送请求之前判断vuex中是否存在token
    // 如果存在,则统一在http请求的header都加上token,这样后台根据token判断你的登录情况
    // 即使本地存在token,也有可能token是过期的,所以在响应拦截器中要对返回状态进行判断
    
    
    const token = window.localStorage.token;

    token && (config.headers.Authorization = `Auth ${token}`);


 
    return config;
  },
  error => {
    return Promise.error(error);
  }
);


// 是否正在刷新的标记
let isRefreshing = false
// 重试队列,每一项将是一个待执行的函数形式
let requests = []
// 响应拦截器
instance.interceptors.response.use(
  response => {
    const res = response.data


    if(res.code === 418){
            const config = response.config
      if (!isRefreshing) {
        isRefreshing = true
       
          
        
        return refreshToken().then(res => {
    
          const { token ,refreshToken} = res.data
         
          
          instance.setToken(token,refreshToken)
          config.headers['Authorization'] = `Auth ${token}`
          config.baseURL = ''
          console.log('token过期刷新接口');
           //  这里有个小问题  当在重试中 如果接口报错 就会直接跳转到登录页  需要后端配合
          // 已经刷新了token,将所有队列中的请求进行重试
          
          requests.forEach(cb => cb(token))
          requests = []
          return instance(config)
        },err=>{
          console.log(err)
          
        }).catch(res => {

         
          removeToken()
          router.push('/login')
          console.error('refreshtoken error =>', res)
         
          
        }).finally(() => {
          console.log('这边');
          
          isRefreshing = false
        })
      }else {
        // 正在刷新token,将返回一个未执行resolve的promise
        // 保存函数 等待执行
        // 吧请求都保存起来 等刷新完成后再一个一个调用
        return new Promise((resolve) => {
          // 将resolve放进队列,用一个函数形式来保存,等token刷新后直接执行
          requests.push((token) => {
            config.baseURL = ''
            config.headers['Authorization'] = `Auth ${token}`
            resolve(instance(config))
          })
        })
        
      }
    
    }  

  
    
 

    
     if (res.code === 0) {
   //  所有失败拦截
      return Promise.reject(res);
      // return res
    }
    return res
  },
  error => {
     console.log(error);
  
    return Promise.reject();
  }
);

export { instance };

优化

问题:

当token过期,调用refreshToken方法后,拿到新的token去发送之前的请求 这个时候如果后端没处理好 返回给你400状态码 就会进入catch然后就需执行重新登录的操作

解决方法:
不需要在catch 进行删除token和跳回登录页操作。当refreshToken过期,后端会返回{code: 419, message: 'refreshToken过期,请重新登录', data: {}} 当然还有 token不合法 token未认证 等情况。

完整代码


import axios from "axios";

const instance = axios.create({
  baseURL: process.env.BASE_API, // url = base url + request url

  // timeout: 5000 // request timeout
  withCredentials: true,
 
});

// 给实例添加一个setToken方法,用于登录后将最新token动态添加到header,同时将token保存在localStorage中
instance.setToken = (token,refreshToken) => {
instance.defaults.headers['Authorization'] = `Auth ${token}`
// 这里用到的存储是localStorage
window.localStorage.setItem('token', token)
  
window.localStorage.setItem('refreshToken', refreshToken)
}

function refreshToken () {

  // 我项目中  更新token 需要吧原有的token 换成refreshToken去请求   这里根据需求可以改动
  window.localStorage.setItem('token', window.localStorage.refreshToken)
  return instance({method:'post',url: '/api-token-refresh'})
}
  // 设置了路由拦截  这里清掉token 就会跳回登录页
function removeToken () {
  window.localStorage.removeItem('token')
  window.localStorage.removeItem('refreshToken')

}

// 请求拦截器
instance.interceptors.request.use(
  config => {
    // 每次发送请求之前判断vuex中是否存在token
    // 如果存在,则统一在http请求的header都加上token,这样后台根据token判断你的登录情况
    // 即使本地存在token,也有可能token是过期的,所以在响应拦截器中要对返回状态进行判断
    
    
    const token = window.localStorage.token;

    token && (config.headers.Authorization = `Auth ${token}`);


 
    return config;
  },
  error => {
    return Promise.error(error);
  }
);


// 是否正在刷新的标记
let isRefreshing = false
// 重试队列,每一项将是一个待执行的函数形式
let requests = []
// 响应拦截器
instance.interceptors.response.use(
  response => {
    const res = response.data
    // refreshToken过期 或者 token不合法 token未认证 
    if(res.code === 419||420||421){
         removeToken()
         router.push('/login')
        return Promise.reject(res);
    }
    
    if(res.code === 418){
            const config = response.config
      if (!isRefreshing) {
        isRefreshing = true
       
          
        
        return refreshToken().then(res => {
    
          const { token ,refreshToken} = res.data
         
          
          instance.setToken(token,refreshToken)
          config.headers['Authorization'] = `Auth ${token}`
          config.baseURL = ''
          console.log('token过期刷新接口');
           //  这里有个小问题  当在重试中 如果接口报错 就会直接跳转到登录页  需要后端配合
          // 已经刷新了token,将所有队列中的请求进行重试
          
          requests.forEach(cb => cb(token))
          requests = []
          return instance(config)
        },err=>{
          console.log(err)
          
        }).catch(res => {

         
         
          console.error('refreshtoken error =>', res)
         
          
        }).finally(() => {
          console.log('这边');
          
          isRefreshing = false
        })
      }else {
        // 正在刷新token,将返回一个未执行resolve的promise
        // 保存函数 等待执行
        // 吧请求都保存起来 等刷新完成后再一个一个调用
        return new Promise((resolve) => {
          // 将resolve放进队列,用一个函数形式来保存,等token刷新后直接执行
          requests.push((token) => {
            config.baseURL = ''
            config.headers['Authorization'] = `Auth ${token}`
            resolve(instance(config))
          })
        })
        
      }
    
    }  

  
    
 

    
     if (res.code === 0) {
   //  所有失败拦截
      return Promise.reject(res);
      // return res
    }
    return res
  },
  error => {
     console.log(error);
  
    return Promise.reject();
  }
);

export { instance };

希望对大家有帮助!!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值