quasar中axios的封装、拦截器、token刷新(笔记)

一、封装axios请求

import libAxios from 'axios'
// axios网络请求封装请求开始
const axios = libAxios.create({
  baseURL: process.env.API_ROOT,
  timeout: 15000,
  withCredentials: false,
  headers: {
    'Content-Type': 'application/json;charset=utf-8',
    'Access-Control-Allow-Headers': 'X-Requested-With,Content-Type',
    'Access-Control-Allow-Methods': 'OPTIONS, GET, POST'
  }
})
export default axios

其中,baseURL的地址在文件quasar.conf.js里配置

build配置里

 env: ctx.dev
        ? {
          // so on dev we'll have
          API_ROOT: JSON.stringify('/v1')
        }
        : {
          // and on build (production):
          API_ROOT: JSON.stringify('http://***.***.**.**:8088/v1')
        }

设置代理devServer

devServer: {
      https: false,
      port: 8080,
      open: true, // opens browser window automatically
      proxy: {
        '/v1': {
          target: 'http://***.***.***.**:8088',
          changeOrigin: true
        }
      }
    },

以上就是axios的封装

使用封装后的axios

配置api,新建目录,新建api文件

//我封装的位置,引进来
import axios from 'src/lib/axios'
export const login = ({ mobile, password}) => {
  const data = {
    mobile,
    password,
  }
  return axios({
    url: '/login',
    method: 'post',
    data
  })
}

使用

<script>
    import { login } from 'src/api/common'
    export default {
       name: 'Login',
       data () {
          return {
            form: {
              mobile: '',
              password: ''
            }
          }
        }, 
        methods:{
           onSubmit () {
                login(this.form).then(data => {
                      console.log(data)
                })
           } 
        }
    }
</script>

 


二、拦截器+token刷新

我这里设置拦截器的目的是实现保持登录功能,我的token一小时过期,所以需要在快过期的时候通过refreshToken(一般十几天过期)重新获取token,当refreshToken过期时提醒用户重新登录或者直接跳到登录页

1.先看一下我的两个接口(登录接口、token刷新接口)所返回的数据,只看一下模拟的接口,后台的具体实现暂且不表

登录接口login——返回access_token作为令牌,refresh_token作为刷新access_token的凭据

token刷新接口refresh

2.登录成功后对返回的数据进行处理

这里我先拿到token,然后存进vuex里,登录成功后跳转首页

这个目录不仅存了token,还存了用户的登录状态等信息

onSubmit () {
        login(this.form).then(data => {
          const { access_token: accessToken, refresh_token: refreshToken } = data
          setAuth({
            accessToken,
            refreshToken
          })
          this.$router.push({
            path: this.$route.query.redirect || '/'
          })
        })
 }

这里介绍怎么设置vuex,我的vuex目录如下,关于token的存储在user目录里

actions.js

先不管getters.js

index.js

mutations.js

state.js

在modules目录下的index.js

在store下的index.js,为了防止刷新后数据消失,我使用了插件vuex-persistedstate,

import Vue from 'vue'
import Vuex from 'vuex'
import modules from './modules'
import createPersistedState from 'vuex-persistedstate'
import SecureLS from 'secure-ls'

const ls = new SecureLS({
  encodingType: 'aes',
  isCompression: false
})

Vue.use(Vuex)

const store = new Vuex.Store({
  modules,

  // enable strict mode (adds overhead!)
  // for dev mode only
  strict: process.env.DEV,
  plugins: [
    createPersistedState({
      storage: {
        getItem: (key) => ls.get(key),
        setItem: (key, value) => ls.set(key, value),
        removeItem: (key) => ls.remove(key)
      },
      paths: [
        'user',
        'obs.showTransList',
        'obs.token'
      ]
    })
  ]
})

export default store

然后在src/lib/utils文件里配置用户状态的存储,这个文件就引入登录页

import * as jwtDecode from 'jwt-decode'//jwt
import store from 'src/store'//vuex

//先放在这,对于状态存储暂时没有用,由于后面token过期的判断
export const decodeJwt = (token, header = false) => {
  return jwtDecode(token, { header })
}

export const setAuth = ({ accessToken, refreshToken }) => {
  const decodedToken = jwtDecode(accessToken)
  console.log('登录状态', decodedToken)
  const user = {
    id: decodedToken.aud,
    exp: decodedToken.exp,
    iat: decodedToken.iat,
    iss: decodedToken.iss,
    jti: decodedToken.jti,
    nickName: decodedToken.nick_name
  }
  store.dispatch('user/setAuth', {
    accessToken,
    refreshToken,
    user
  })
}

以上就是对login接口返回的token数据进行存储的全部过程

3.设置拦截器拦截请求和响应,这里的拦截器主要为token刷新准备,在设置拦截器之前选配置好token刷新api、获取token、token过期判断等,现在一步步来

(1)刷新token的api,我放在配置login api的文件里

// 刷新token
export const refreshToken = (refreshToken) => {
  const data = {
    refresh_token: refreshToken
  }
  return axios({
    url: '/refresh',
    method: 'post',
    data
  })
}

(2)关于token过期、获取token、清除token等的配置放在文件src/lib/utils里

  • 读取token
//读取放在vuex里的内容
export const getToken = () => {
  return store.state.user.token || {}
}
  • 删除token
export const removeAuth = () => {
  store.dispatch('user/removeAuth')
}
  • 判断accessToken是否(即将)过期,这里就用到之前暂放的decodeJwt函数
export const isTokenExpired = () => {
  const { accessToken } = getToken()
  if (!accessToken) {
    return false
  }
  const decodedToken = decodeJwt(accessToken)
  // 小于0已过期||小于10分钟即将过期
  return parseInt(decodedToken.exp) - (Date.now() / 1000) < 10 * 60
}
  • 判断refreshToken是否过期
export const isRefreshTokenExpired = () => {
  const { refreshToken } = getToken()
  if (!refreshToken) {
    return true
  }
  const expiredTime = parseInt(refreshToken.slice(refreshToken.lastIndexOf('_') + 1))
  const nowTime = Date.now() / 1000
  // 检验本地时间差
  return expiredTime < nowTime
}

(3)在axios.js文件里引入配置内容

须在axios.js引入这些文件,其中simpleNotify是警告提示的作用,暂时不表

引入

 (4)拦截器的前一步准备

(5)拦截器配置——请求拦截

// 拦截请求
axios.interceptors.request.use(
  config => {
    // 全局loading
    LoadingBar.increment(0.5)
    LoadingBar.start()
    //获取token
    const { accessToken, refreshToken } = getToken()

    //accexxtoken是否存在
    if (accessToken) {
      //给请求添加token令牌
      config.headers.Authorization = `Bearer ${accessToken}`

      // 判断refershToken是否过期,过期就给出重新登录提示
      if (isRefreshTokenExpired()) {
        LoadingBar.stop()
        manualLogin()
        return Promise.reject('refreshToken expired')
      }

      // 主动刷新token——在accesstoken临近过期的时候
      if (isTokenExpired() && !config.url.includes('/refresh') && !config.url.includes('/login')) {
        // 判断是否正在刷新token
        if (!window.isRefreshing) {
          console.log('主动刷新token')
          window.isRefreshing = true
          //请求token刷新接口,获取新的accesstoken
          getRefreshToken(refreshToken)
            .then(data => {
              const { access_token: accessToken, refresh_token: refreshToken } = data
              if (!accessToken) {
                throw new Error('刷新失败')
              }
              console.log('token主动刷新成功: ', data)
              window.isRefreshing = false
              //将获取的新accesstoken加在请求上
              config.headers.Authorization = `Bearer ${accessToken}`
              
              //并保存在vuex里
              setAuth({
                accessToken,
                refreshToken
              })

              // 执行刷新token期间挂起的api请求
              onRefreshed(accessToken)
              // 执行onRefreshed函数后清空挂起的请求
              refreshSubscribers = []
            })
            .catch(err => {
              console.log('token主动刷新失败', err)
              manualLogin()
            })
        }

        // 把token刷新期间的每一个请求都放在数组中
        return new Promise((resolve) => {
          subscribeTokenRefresh((token) => {
            config.headers.Authorization = `Bearer ${token}`
            // 将请求挂起
            resolve(config)
          })
        })
      }
    }
    return config
  },
  error => {
    console.log('request error: ', error)
    simpleNotify()
    return Promise.reject(error)
  }
)

(6)拦截器配置——响应拦截

// 拦截响应
axios.interceptors.response.use(
  response => {
    LoadingBar.stop()
    // 处理响应
    const { config, data, data: { code, message } } = response
    //200 成功
    if (code === 200) {
      return Promise.resolve(data.data)
    } else if (code === 401) {
      // token认证失败,被动刷新token
      //获取refreshToken —— 用作获取新accesstoken的凭据
      const { refreshToken } = getToken()

      if (refreshToken) {
        if (!config.url.includes('/refresh')) {
          if (!window.isRefreshing) {
            console.log('被动刷新token')
            window.isRefreshing = true
            getRefreshToken(refreshToken)
              .then((data) => {
                const { access_token: accessToken, refresh_token: refreshToken } = data
                if (!accessToken) {
                  throw new Error('刷新失败')
                }
                console.log('token被动刷新成功: ', data)
                window.isRefreshing = false
                config.headers.Authorization = `Bearer ${accessToken}`
                setAuth({
                  accessToken,
                  refreshToken
                })

                // 执行刷新token期间挂起的api请求
                onRefreshed(accessToken)
                // 执行onRefreshed函数后清空挂起的请求
                refreshSubscribers = []
              })
              .catch((err) => {
                console.log('token被动刷新失败', err)
                // token被动刷新失败
                manualLogin()
              })
          }
          // 把token刷新期间的每一个请求都挂起
          return new Promise((resolve) => {
            subscribeTokenRefresh((token) => {
              config.url = config.url.replace(config.baseURL, '')
              config.headers.Authorization = `Bearer ${token}`
              // 重新请求
              axios(config)
            })
          })
        }
      } else {
        manualLogin()
      }
    } else {
      simpleNotify({ message })
      return Promise.reject(data)
    }
  },
  error => {
    LoadingBar.stop()
    console.log(error)
    return Promise.reject(error)
  }
)

 


版权声明:本文为qq_42257997原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。