使用axios实现vue web前端无痕刷新

avatar
作者
筋斗云
阅读量:0

本文使用刷新接口去实现Token的无痕刷新,当拦截器检测到接口返回401响应码(即:认证失败),在拦截器中调用刷新接口去刷新Token,如果返回402(即:刷新token失效)则跳转到登录页,否则就更新Token信息。

定义相关接口

import request from '@/utils/request'  export function login(data) {   return request({     url: '/api/user/login',     method: 'post',     data   }) }  export function getInfo() {   return request({     url: '/api/user/info',     method: 'get'   }) }  export function tokenRefresh(data) {   return request({     url: '/api/user/token/refresh',     method: 'post',     data   }) }  export function logout() {   return request({     url: '/api/user/logout',     method: 'post'   }) } 

Token信息

{     "code": 200,     "msg": "成功",     "data": {         "accessToken": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1aWQiOjEsInJvbGVJZHMiOiIxIiwibmFtZSI6ImFkbWluIiwiZGVwdElkIjoxLCJ0eXBlIjowLCJqdGkiOiI5Y2Y3MzAwMC00YjNiLTRjYzAtYTI1YS01ZDM5YjFhYmFlMDgiLCJleHAiOjE3MjA2ODkyNTAsImlhdCI6MTcyMDY4OTE5MCwic3ViIjoiUGVyaXBoZXJhbHMiLCJpc3MiOiJPY2VhbiJ9.0-E4S5NuXNZCNslhyVpBjFZVCIqDahn6wdBt0gAxxmU",         "accessTokenExpireIn": 1720689250,         "refreshToken": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1aWQiOjEsInJvbGVJZHMiOiIxIiwibmFtZSI6ImFkbWluIiwiZGVwdElkIjoxLCJ0eXBlIjoxLCJqdGkiOiIwZDEzMWUzNC1lOGNiLTRhZjItOTMyZC1iN2JlNzQzZDgzZjgiLCJleHAiOjE3MjA2OTYzOTAsImlhdCI6MTcyMDY4OTE5MCwic3ViIjoiUGVyaXBoZXJhbHMiLCJpc3MiOiJPY2VhbiJ9.TBfL8cNciiS9GbX-2T1ejWWHYdyrUzkJELixAcm8EJ0",         "refreshTokenExpireIn": 1720696390     } } 

定义前端拦截器 request.js

import axios from 'axios' import { Message } from 'element-ui' import { getToken, getRefreshToken, setToken, removeToken } from '@/utils/auth' import { tokenRefresh } from '@/api/user'  // 创建 axios 实例 const service = axios.create({   // 请求地址前缀   baseURL: process.env.VUE_APP_BASE_API,   // 请求5s超时   timeout: 5000 // request timeout })  // Request 拦截器 service.interceptors.request.use(      config => {      // 从cookie中获取令牌信息,并放入接口头信息中     const token = getToken()     if (token) {       config.headers['Authorization'] = token     }      return config   },   error => {     // do something with request error     console.log(error) // for debug     return Promise.reject(error)   }  )  // 刷新令牌的标记 let isRefreshing = false  // 重试请求队列 let requests = []  // Response 拦截器 service.interceptors.response.use(      response => {          const res = response.data     if (res.code === 200) {       return res     }      if(res.code === 402) {       removeToken()       location.reload()     }      // 401: 认证失败,402:令牌过期     if(res.code === 401) {        const refreshToken = getRefreshToken()       if(!isRefreshing) {                  isRefreshing = true         // 刷新令牌,刷新成功后进行客户端令牌更新         return tokenRefresh( {refreshToken: refreshToken} ).then(result => {                      const data = result.data           setToken(data.accessToken, data.refreshToken)            const token = data.accessToken           response.config.headers['Authorization'] = token           // token 刷新后将数组的方法重新执行           requests.forEach((request) => request(token))           // 重新请求完清空           requests = []             return service(response.config)         }).catch(() => {           removeToken()           location.reload()         }).finally(() => {           isRefreshing = false         })        } else {         // 返回未执行 resolve 的 Promise         return new Promise(resolve => {           // 用函数形式将 resolve 存入,等待刷新后再执行           requests.push(token => {             response.config.headers['Authorization']  = token             resolve(service(response.config))           })         })       }     }      Message({       message: res.msg || 'Error',       type: 'error',       duration: 5 * 1000     })      return Promise.reject(new Error(res.msg || 'Error'))   },   error => {     console.log('err' + error) // for debug     Message({       message: error.msg,       type: 'error',       duration: 5 * 1000     })          return Promise.reject(error)   } )  export default service 

定义Token校验路由跳转 permission.js

import router from './router' import store from './store' import { Message } from 'element-ui' import NProgress from 'nprogress' // progress bar import 'nprogress/nprogress.css' // progress bar style import { getToken } from '@/utils/auth' // get token from cookie import getPageTitle from '@/utils/get-page-title'  NProgress.configure({ showSpinner: false }) // NProgress Configuration  const whiteList = ['/login'] // no redirect whitelist  router.beforeEach(async(to, from, next) => {   // start progress bar   NProgress.start()    // set page title   document.title = getPageTitle(to.meta.title)    // determine whether the user has logged in   const hasToken = getToken()    if (hasToken) {     if (to.path === '/login') {       // if is logged in, redirect to the home page       next({ path: '/' })       NProgress.done()     } else {       const hasGetUserInfo = store.getters.name       if (hasGetUserInfo) {         next()       } else {         try {           // get user info           await store.dispatch('user/getInfo')            next()         } catch (error) {           // remove token and go to login page to re-login           await store.dispatch('user/resetToken')           Message.error(error || 'Has Error')           next(`/login?redirect=${to.path}`)           NProgress.done()         }       }     }   } else {     /* has no token*/      if (whiteList.indexOf(to.path) !== -1) {       // in the free login whitelist, go directly       next()     } else {       // other pages that do not have permission to access are redirected to the login page.       next(`/login?redirect=${to.path}`)       NProgress.done()     }   } })  router.afterEach(() => {   // finish progress bar   NProgress.done() }) 

Token前端缓存 auth.js

const TokenKey = 'accessToken' const RefreshTokenKey = 'refreshToken'  export function getToken() {   return localStorage.getItem(TokenKey) }  export function getRefreshToken() {   return localStorage.getItem(RefreshTokenKey) }  export function setToken(accessToken, refreshToken) {   localStorage.setItem(TokenKey, accessToken)   localStorage.setItem(RefreshTokenKey, refreshToken) }  export function removeToken() {   localStorage.removeItem(TokenKey)   localStorage.removeItem(RefreshTokenKey) } 

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!