zoukankan      html  css  js  c++  java
  • 封装 axios 拦截器实现用户无感刷新 access_token

    这里使用axios,其中做的是请求后拦截,所以用到的是axios的响应拦截器axios.interceptors.response.use()方法。

    @utils/auth.js
    
    import Cookies from 'js-cookie'
    
    const TOKEN_KEY = 'access_token'
    const REGRESH_TOKEN_KEY = 'refresh_token'
    
    export const getToken = () => Cookies.get(TOKEN_KEY)
    
    export const setToken = (token, params = {}) => {
      Cookies.set(TOKEN_KEY, token, params)
    }
    
    export const setRefreshToken = (token) => {
      Cookies.set(REGRESH_TOKEN_KEY, token)
    }
    
    @request.js
    
    import axios from 'axios'
    import { getToken, setToken, getRefreshToken } from '@utils/auth'
    
    // 刷新 access_token 的接口
    const refreshToken = () => {
      return instance.post('/auth/refresh', { refresh_token: getRefreshToken() }, true)
    }
    
    // 创建 axios 实例
    const instance = axios.create({
      baseURL:  process.env.GATSBY_API_URL,
      timeout: 30000,
      headers: {
        'Content-Type': 'application/json',
      }
    })
    
    let isRefreshing = false // 标记是否正在刷新 token, 防止多次刷新token
    let requests = [] // 存储待重发请求的数组(同时发起多个请求的处理)
    
    instance.interceptors.response.use(response => {
        return response
    }, error => {
        if (!error.response) {
            return Promise.reject(error)
        }
        if (error.response.status === 401 && !error.config.url.includes('/auth/refresh')) {
            const { config } = error
            if (!isRefreshing) {
                isRefreshing = true
                return refreshToken().then(res=> {
                    const { access_token } = res.data
                    setToken(access_token)
                    config.headers.Authorization = `Bearer ${access_token}`
                    // token 刷新后将数组的方法重新执行
                    requests.forEach((cb) => cb(access_token))
                    requests = [] // 重新请求完清空
                    return instance(config)
                }).catch(err => {
                    console.log('抱歉,您的登录状态已失效,请重新登录!')
                    return Promise.reject(err)
                }).finally(() => {
                    isRefreshing = false
                })
            } else {
                // 返回未执行 resolve 的 Promise
                return new Promise(resolve => {
                    // 用函数形式将 resolve 存入,等待刷新后再执行
                    requests.push(token => {
                        config.headers.Authorization = `Bearer ${token}`
                        resolve(instance(config))
                    })  
                })
            }
        }
        return Promise.reject(error)
    })
    
    // 给请求头添加 access_token
    const setHeaderToken = (isNeedToken) => {
      const accessToken = isNeedToken ? getToken() : null
      if (isNeedToken) { // api 请求需要携带 access_token 
        if (!accessToken) { 
          console.log('不存在 access_token 则跳转回登录页')
        }
        instance.defaults.headers.common.Authorization = `Bearer ${accessToken}`
      }
    }
    
    // 有些 api 并不需要用户授权使用,则无需携带 access_token;默认不携带,需要传则设置第三个参数为 true
    export const get = (url, params = {}, isNeedToken = false) => {
      setHeaderToken(isNeedToken)
      return instance({
        method: 'get',
        url,
        params,
      })
    }
    
    export const post = (url, params = {}, isNeedToken = false) => {
      setHeaderToken(isNeedToken)
      return instance({
        method: 'post',
        url,
        data: params,
      })
    }
    

    当账户(access_token)过期了, 会自动触发refresh_token,重新获取access_token

  • 相关阅读:
    mac 个人账户环境变量位置
    版本控制工具Git
    xmind导图用例转换为表格用例
    Mysql清空表(truncate)与删除表中数据(delete)的区别
    log4j日志级别及使用规范
    洛谷 P3807 【模板】卢卡斯定理
    jmeter环境配置与java环境配置
    NOI2020乱搞记
    Re:ゼロから始める文化課生活
    阅读书单
  • 原文地址:https://www.cnblogs.com/zpsakura/p/13685568.html
Copyright © 2011-2022 走看看