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.具体截图 页面 请求
相关推荐
慧一居士29 分钟前
flex 布局完整功能介绍和示例演示
前端
DoraBigHead31 分钟前
小哆啦解题记——两数失踪事件
前端·算法·面试
一斤代码6 小时前
vue3 下载图片(标签内容可转图)
前端·javascript·vue
中微子6 小时前
React Router 源码深度剖析解决面试中的深层次问题
前端·react.js
光影少年6 小时前
从前端转go开发的学习路线
前端·学习·golang
中微子7 小时前
React Router 面试指南:从基础到实战
前端·react.js·前端框架
3Katrina7 小时前
深入理解 useLayoutEffect:解决 UI "闪烁"问题的利器
前端·javascript·面试
前端_学习之路8 小时前
React--Fiber 架构
前端·react.js·架构
伍哥的传说8 小时前
React 实现五子棋人机对战小游戏
前端·javascript·react.js·前端框架·node.js·ecmascript·js
qq_424409198 小时前
uniapp的app项目,某个页面长时间无操作,返回首页
前端·vue.js·uni-app