实现逻辑如下

1、对于每次请求数据,从cookie中取token并赋值到请求头上(headers.Authorization)

2、对后端返回的数据,如果返回401,则通过存储在cookie中的刷新令牌(refreshTokenKey)来刷新token值,(一般而言token有效期7天或者一天,刷新令牌比token存放的久)

3、如果刷新令牌存在,则通过刷新令牌,重新获取用户信息,包括,token,userInfo(用户信息),refreshToken(刷新令牌)

4、如果刷新令牌不存在,则重新登录

(刷新页面,Vuex状态变成初始值)

发送请求

service.interceptors.request.use(
  config => {
    //  获取token
    const accessToken = PcCookie.get(Key.accessTokenKey)

    if (accessToken) {
      // 如果有token,则通过请求头添加
      config.headers.Authorization = `Bearer ${accessToken}`
      // 也可以使用下面这种
      // config.headers['Authorization'] = `Bearer ${accessToken}`

      // 后台接收的key:Authorization value值: Bearer ${accessToken}

    }
    return config
  },
  error => {
    // do something with request error
    console.log(error) // for debug
    return Promise.reject(error)
  }
)

接收后台返回数据

service.interceptors.response.use(

  response => {
    const res = response.data

    // if the custom code is not 20000, it is judged as an error.
    if (res.code !== 20000) {
   //写正常返回逻辑
  },
  error => {
    console.log('err' + error) // for debug

    // 判断状态
    if (error.response && error.response.status !== 401) {
      Message({
        message: error.message,  
        type: 'error',
        duration: 5 * 1000
      })
      return Promise.reject(error)
    } 
    // 如果是401未认证,则通过刷新令牌获取状态信息
    let isLock = true//防止重复提交
    if (isLock && PcCookie.get(Key.refreshTokenKey)) {
      isLock = false
      // 跳转到统一认证终端。实现令牌刷新token的效果
      window.location.href = `${process.env.VUE_APP_AUTH_CENTER_URL}/refesh?/redirectURL=${window.location.href}`
    } else {
      // 如果刷新令牌没有,则进入登录页面
      window.location.href = `${process.env.VUE_APP_AUTH_CENTER_URL}/login?/redirectURL=${window.location.href}`
    }

  }
)

以上已经实现请求时候带上token值

Vuex菜单,按钮权限状态管理 

vuex 权限菜单集合,根据 vuex 中可访问的菜单,渲染侧边栏组件

创建menu.js

在store下面的module文件夹下面创建menu.js文件如下

// 请求用户权限接口API
import { getUserMenuList } from "@/api/user"

import { PcCookie, Key } from '@/utils/cookie'
const state = {
    init: false,//是否已经加载用户权限
    menuList: [],//用户拥有的菜单权限
    buttonList: [],//用户拥有的按钮权限
}
const mutations = { //改变状态值
    SET_SYSTEM_MENU: (state, data) => {
        state.init = true//已经加载用户权限
        state.menuList = data.menuTreeList//保存菜单权限
        state.buttonList = data.buttonList//保存按钮权限
    }

}
// 定义行为
const actions = {
    GetUserMenu({ commit }) {
        // reslove正常返回结果,reject异常返回结果
        return new Promise((reslove, reject) => {
            //获取用户ID
            const userId = PcCookie.get(Key.userInfoKey) ? JSON.parse(PcCookie.get(Key.userInfoKey)).uid : null
            // 发送请求获取权限信息
            if (userId) {
                getUserMenuList(userId).then(response => {
                    // 将获取到的菜单按钮数据信息,存放到Vuex进行状态管理
                    commit('SET_SYSTEM_MENU', response.data)
                    reslove()
                }).catch(error => {
                    // 返回异常对象
                    reject()
                })
            }

        })
    }
}
export default {
    namespaced: true,//引用需要的模块名称 /menu/GetUserMenu
    state,
    mutations,
    actions
}

在getters.js中引入state中定义的几个状态,外面使用方式 {{$store.getters.buttonList}}

const getters = {
 ........
  // 添加菜单相关状态
  init: state => state.menu.init,
  menuList: state => state.menu.menuList,
  buttonList: state => state.menu.buttonList
}
export default getters

在index,js中引入menu.js给vuex状态管理

import Vue from 'vue'
import Vuex from 'vuex'
import getters from './getters'
import app from './modules/app'
import settings from './modules/settings'
import user from './modules/user'
// tagsView
import tagsView from './modules/tagsView'
// 
import menu from './modules/menu'

Vue.use(Vuex)

const store = new Vuex.Store({
  modules: {
    app,
    settings,
    user,
    tagsView,
    menu
  },
  getters
})

export default store

 

request header 添加token header设置token_数据

在permission中对,存在token值且没有拿取到菜单,按钮的信息,就调用 store.dispatch('menu/GetUserMenu')方法进行拿取,只要不刷新页面,vuex就会存在菜单按钮信息,通过store.getters.init 判断,就不用每次请求路由都调用(刷新页面置空Vuex数据)

const hasGetUserInfo = PcCookie.get(Key.accessTokenKey)
      if (hasGetUserInfo) {
        // 如果有用户信息,则通过用户ID来查询当前用户的菜单和按钮权限

        if (store.getters.init === false) {

          // 还没有查询用户信息,开始查询用户信息
          //因为我们采用namespaced: true,所以需要
          store.dispatch('menu/GetUserMenu').then(() => {
            //继续访问目标路由,且不会留下history记录
            next({ ...to, replace: true })
          }).catch(error => {
            Message({
              message: "获取用户信息失败",
              type: "error"
            })
          })
        } else {
          // 跳转到目标路由
          next()
        }

通过Vuex中的管理的数据,动态渲染左边的菜单组件

<sidebar-item v-for="menu in menuList" :key="menu.id" :item="menu"  />

import { mapGetters } from 'vuex'
export default {
  components: { SidebarItem, Logo },
  computed: {
    ...mapGetters([
      'sidebar',
      'menuList'//获取menuList中的状态值,menuList
    ]),
<div>
    <!-- 没有子菜单,只有一级菜单 -->
    <template v-if="!item.children || item.children.length === 0">
      <app-link :to="item.url">
        <el-menu-item
          :index="item.url"
          :class="{ 'submenu-title-noDropdown': !isNest }"
        >
          <item :icon="item.icon" :title="item.name" />
        </el-menu-item>
      </app-link>
    </template>
    <!-- 有子菜单 -->
    <el-submenu v-else ref="subMenu" :index="item.id" popper-append-to-body>
      <template slot="title">
        <item :icon="item.icon" :title="item.name" />
      </template>
      <sidebar-item
        v-for="child in item.children"
        :key="child.id"
        :is-nest="true"
        :item="child"
        
        class="nest-menu"
      />
      <!-- :base-path="resolvePath(child.path)" -->
    </el-submenu>
  </div>