• 封装 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

  • 相关阅读:
    来看看面试必问的HashMap,一次彻底帮你搞定HashMap源码
    深入浅出!springboot从入门到精通,实战开发全套教程!
    讲一讲Java的字符串常量池,看完你的思路就清晰了
    面向对象的这些核心技术,你掌握后包你面试无忧
    他凭借这70份PDF,3170页文件,成功斩获了含BATJ所有的offer
    springboot实战开发全套教程,让开发像搭积木一样简单!Github星标已上10W+!
    这行代码告诉你!为什么你地下城与勇士(DNF)的装备强化老是失败?
    精益求精!Spring Boot 知识点全面回顾,带你重新细读源码!
    太妙了!Spring boot 整合 Mybatis Druid,还能配置监控?
    putchar(".:-=+*#%@"[(int)(d * 5.0f)])
  • 原文地址:https://www.cnblogs.com/zpsakura/p/13685568.html
Copyright © 2020-2023  润新知