vue 无感刷新token

何为双 token

- accessToken : 用户获取数据权限

- refreshToken : 用来获取新的accessToken

双 token 验证机制,其中 accessToken 过期时间较短,refreshToken 过期时间较长。当 accessToken 过期后,使用 refreshToken 去请求新的 token。

无感刷新:

当客户端检测到access token即将过期或已经过期时,自动在后台向认证服务器发起请求,携带refresh token换取新的access token。这个过程对用户来说是无感知的,即用户不需要重新登录,页面也不会中断或刷新,因此被称为"无感刷新"。

实现方式:

当 accessToken 过期时,调接口,会返回201,表示token过期,这个时候需要调用 refreshToken 接口,重新获取新的token,在这个期间执行的请求,都存放到一个新的请求队列中,当获取新的token之后再重新调用,接口返回202的时候表示 refreshToken 过期,需要给用户提示,重新登录页面,跳转到登录页

具体步骤:

1.先再store 中定义接口,存储 refreshToken 和 accessToken ,定义刷新token接口,修改 refreshToken 和 accessToken
js 复制代码
import { login,refreshToken} from '@/api/login'

const user = {
  state: {
    access_token: getStore({
      name: 'access_token'
    }) || '',
    refresh_token: getStore({
      name: 'refresh_token'
    }) || '',
  },

  mutations: {
    SET_ACCESS_TOKEN(state, token) {
      state.access_token = token
      setStore({
        name: 'access_token',
        content: state.access_token,
        type: 'session'
      })
    },
    SET_REFRESH_TOKEN: (state, rfToken) => {
      state.refresh_token = rfToken
      setStore({
        name: 'refresh_token',
        content: state.refresh_token,
        type: 'session'
      })
    },
  },

  actions: {
    // 登录
    Login({ commit }, userInfo) {
      const loginname = userInfo.loginname.trim()
      const password = userInfo.password
      return new Promise((resolve, reject) => {
        login(loginname, password).then(res => {
          clearStore()
          clearStore({ type: 1 })
          commit('SET_ACCESS_TOKEN', res.data.accessToken)
          commit('SET_REFRESH_TOKEN', res.data.refreshToken)
          resolve()
        }).catch(error => {
          reject(error)
        })
      })
    },
    // 刷新token
    RefreshToken({ commit, state }) {
      return new Promise((resolve, reject) => {
        refreshToken({refreshToken:state.refresh_token}).then(res => {
          if(res.success){
            commit('SET_ACCESS_TOKEN', res.data.accessToken)
            commit('SET_REFRESH_TOKEN', res.data.refreshToken)
          } 
          resolve(res)
        }).catch(error => {
          reject(error)
        })
      })
    },
  }
}


export default user
2.再在请求拦截器中针对不同返回值来处理token情况
js 复制代码
//1引入
import axios from 'axios'
import NProgress from 'nprogress' // progress bar
import { Message, MessageBox, Notification } from 'element-ui'
import store from '@/store'
import errorCode from '@/util/errorCode'
import 'nprogress/nprogress.css'
import { baseURL, buildEnv, env } from './baseURL'

axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
//2创建axios实例
let service = axios.create({
    baseURL: baseURL[buildEnv],
    timeout: 6000
})
// 返回其他状态吗
service.defaults.validateStatus = function (status) {
    return status >= 200 && status <= 500 // 默认的
}

// 跨域请求,允许保存cookie
service.defaults.withCredentials = true
// NProgress Configuration
NProgress.configure({
    showSpinner: false
})

//创建拦截器
service.interceptors.request.use(
    config => {
        NProgress.start()
        // 是否需要设置 token
        const isToken = (config.headers || {}).isToken === false
        const token = store.getters.access_token
        if (token && !isToken) {
            config.headers['Authorization'] = token// 让每个请求携带自定义token 
        }
        return config
    },
    error => {
        console.log(error);
        return Promise.reject(error);
    }
)

//标志当前是否正在刷新token
var isRefreshing = true
//请求队列
var requests = []
service.interceptors.response.use(
    response => {
        NProgress.done()
        let res = response.data;
        const config = response.config;
        if (!res.success) {
            // 未设置状态码则默认成功状态
            const code = Number(res.errorCode)
            // 获取错误信息
            const msg = res.errorMsg || errorCode[code] || errorCode.default['default']
            // 令牌无效 
            if (code === 200 || code === 202) {
                logout()
            } else if (code === 201) {
                //访问令牌过期
                if (isRefreshing) {
                    isRefreshing = false
                    store.dispatch('RefreshToken')
                        .then(res => {
                            if (res.success) {
                                // 执行失效函数
                                requests.forEach((cb) => cb())
                                //重新请求完清空
                                requests = []
                                return service(config)
                            }
                        })
                        .finally(() => {
                            isRefreshing = true;
                        });
                }
                // 返回未执行 resolve 的 Promise
                return new Promise(resolve => {
                    // 用函数形式将 resolve 存入,等待刷新后再执行
                    requests.push(() => {
                        resolve(service(config));
                    });
                });
            }
            else {
                Message({
                    message: msg,
                    type: 'error'
                })
                return Promise.resolve(res)
            }

        } 
        return Promise.resolve(res)
    },
    error => {
        NProgress.done()
        let { message } = error;
        Message({
            message: message,
            type: 'error',
            duration: 5 * 1000
        })
        return Promise.reject(error)
    }
)


// 退出
const logout = () => {
    MessageBox({
        message: '登录状态已过期,请重新登录',
        type: 'error',
        lockClickModal: false // 设置为false,点击弹窗外围不关闭弹窗
    }).then(() => {
        store.dispatch('LogOut').then(() => {
            store.commit('cleanMenu')
            // 刷新登录页面,避免多次弹框
            window.location.reload()
        })
    })
}
export default service
3.具体截图 页面 请求
相关推荐
yuanyxh6 小时前
Mac 软件推荐
前端·javascript·程序员
万少6 小时前
AtomCode开发微信小程序《谁去呀》 全流程
前端·javascript·后端
某人辛木6 小时前
Web自动化测试
前端·python·pycharm·pytest
Kagol6 小时前
Superpowers GSD gstack AgentSkills深度测评
前端·人工智能
excel7 小时前
JavaScript 字符串与模板字面量:从表象到本质理解
前端
京东云开发者8 小时前
当AI成为导演-如何用AI创作动漫短剧
前端
李白的天不白8 小时前
使用 SmartAdmin 进行前后端开发
java·前端
乘风gg8 小时前
🤡PUA AI Coding 工具 的 10 条终极语录
前端·ai编程·claude
学Linux的语莫8 小时前
Vue 3 入门教程
前端·javascript·vue.js
怕浪猫9 小时前
第一章、Chrome DevTools Protocol (CDP) 详解
前端·javascript·chrome