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.具体截图 页面 请求
相关推荐
柏箱几秒前
使用JavaScript写一个网页端的四则运算器
前端·javascript·css
TU^1 分钟前
C语言习题~day16
c语言·前端·算法
学习使我快乐013 小时前
JS进阶 3——深入面向对象、原型
开发语言·前端·javascript
bobostudio19953 小时前
TypeScript 设计模式之【策略模式】
前端·javascript·设计模式·typescript·策略模式
黄尚圈圈4 小时前
Vue 中引入 ECharts 的详细步骤与示例
前端·vue.js·echarts
浮华似水5 小时前
简洁之道 - React Hook Form
前端
正小安7 小时前
如何在微信小程序中实现分包加载和预下载
前端·微信小程序·小程序
_.Switch9 小时前
Python Web 应用中的 API 网关集成与优化
开发语言·前端·后端·python·架构·log4j
一路向前的月光9 小时前
Vue2中的监听和计算属性的区别
前端·javascript·vue.js