欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Vue前端访问控制方案

程序员文章站 2022-03-06 10:36:45
通过dom元素的class来标识与访问控制相关的dom元素,目的是确定需要进行权限控制的组件范围,再通过dom的id对应后端权限设置的功能节点的domKey,从而实现前后端联动的权限控制。 ......

1、前端访问控制的常规处理方法

  前端访问控制,一般针对界面元素dom element进行可见属性或enable属性进行控制,有权限的,相关元素可见或使能;没权限的,相关元素不可见或失能。这样用户可以明确哪些是无权访问的。可见属性要比使能属性更广泛,这是每个dom元素都有的属性。

  当然前端控制仅仅是整体访问控制的一部分,后端还需要进一步针对接口访问进行鉴权。因为通过编辑浏览器的界面元素的属性,可以绕过前端控制。

  在vue中,也有通过控制路由来实现访问控制的,但没有控制界面元素的情况下,用户体验不是很好。

  本文给出了vue框架下前端访问控制的整体方案。

2、总体方案

  在用户登录时,或权限变更时,后端通过接口将权限树发给前端。为了减少不必要的数据传输,后端发出的权限树仅包括有权限的功能项,即前端收到的权限树的各个节点都是有权限的功能项。

  权限树节点的数据部分即为功能项的权限信息,包括两个关键字段:url和domkey。url是后端自己使用,在aop鉴权切面类中,拦截非法的接口访问。domkey是给前端使用的,即dom element的id值,domkey的确定需要前后端协商一致,不能搞错。

  domkey在同一个路径上,不允许重复;不同路径,允许重复。所谓路径,是从根节点开始,到该节点的一系列节点组成的树杈。当然,没有必要的话,domkey最好不重复。同一个界面视图范围的各子节点的domkey也不允许重复。

  前端本地存储用户token和权限树json字符串,如果本地这个存储信息存在,重新打开浏览器,可以免登录。(仅本地token有效,不能完全保证token真的有效,如后端重启服务器、token过期等导致token失效,前端通过http访问时,仍然会跳到登录页面)。

  登录成功后,将token和权限树json字符串保存到本地存储。

  权限发生变更时,通过response拦截器,检查有无附加信息,如有需要,更新token和权限树json字符串。

  前端开发一个权限树的管理的js文件,用于权限树json对象的访问,权限树json字符串被转换成权限树json对象。

  开发前端页面vue文件时,需要进行权限控制的dom element,使用下列属性:

class="permissions" id="相关domkey"

  通过class来标识该界面元素是与访问控制相关的,目的是确定需要进行权限控制的组件范围,id即为该功能项对应的domkey。

  然后,使用一个公共权限设置方法,来统一处理权限相关的界面元素。

  由于vue的组件style,可以有scoped属性设置,此时,在app.vue中,就不能访问到相关dom element的class,局部式样渲染后,在外部被改写,因此,在scoped限制的情况下,需要在scoped起作用的vue组件中,也要调用公共权限设置方法。另外,scoped的限制,恰好使得相同domkey的节点,可以通过上级节点domkey来加以区分。这样,就用统一的方法,实现了前端页面的访问控制。

3、方案实现

3.1、功能项的表结构定义

drop table if exists `function_tree`;
create table `function_tree`
(
  `func_id`       int(11)      not null default 0 comment '功能id',
  `func_name`     varchar(100) not null default '' comment '功能名称',
  `parent_id`     int(11)      not null default 0 comment '父功能id',
  `level`         tinyint(4)   not null default 0 comment '功能所在层级',
  `order_no`	  int(11)      not null default 0 comment '显示顺序',
  `url`			  varchar(80) not null default '' comment '访问接口url',
  `dom_key`       varchar(80) not null default '' comment 'dom对象的id',

  `remark`        varchar(200) not null default '' comment '备注',

  -- 记录操作信息
  `operator_name` varchar(80)  not null default '' comment '操作人账号',
  `delete_flag`   tinyint(4)   not null default 0 comment '记录删除标记,1-已删除',
  `create_time`   datetime(3)  not null default now(3) comment '创建时间',
  `update_time`   datetime(3)           default null on update now(3) comment '更新时间',
  primary key (`func_id`)
) engine = innodb
  default charset = utf8 comment ='功能表';

  如有需要,可以增加icon字段,用于前端树节点的显示。

3.2、后端权限树的输出

  后端在登录成功后,给前端发送token和权限树json字符串。

  关于树节点的生成,可参阅:java通用树结构数据管理---https://www.cnblogs.com/alabo1999/p/14928380.html。里面有关于权限树的例子。

  为了方便前端管理,这里修改权限树的输出,将根节点也一并输出到前端。

  在管理员修改用户权限后,动态权限更新,可通过附加信息,给前端发送token和权限树json字符串。参阅:spring boot动态权限变更实现的整体方案---https://www.cnblogs.com/alabo1999/p/14948914.html。

3.3、前端本地缓存

  vue项目中,新建/src/store目录,创建inde.js文件。代码如下:

import vue from 'vue';
import vuex from 'vuex';
vue.use(vuex);
 
const store = new vuex.store({
 
  state: {
    // 存储token
    token: localstorage.getitem('token') ? localstorage.getitem('token') : '',
    // 存储权限树
    rights: localstorage.getitem('rights') ? localstorage.getitem('rights') : ''
  },

  mutations: {
    // 修改token,并将token存入localstorage
    changelogin (state, user) {
      if(user.token){
        state.token = user.token;
        localstorage.setitem('token', user.token);  
      }
      if (user.rights){
        state.rights = user.rights;
        localstorage.setitem('rights', user.rights);
      }
    }
  }
});
 
export default store;

3.4、创建权限管理模块

  vue项目中,新建/src/common目录,创建treenode.js文件。代码如下:

/**
 * 处理树结构数据,这里主要指功能权限树
 * 权限树的结构如下:
 * [
 *   {
 *      nodedata:{
 *          funcid:1,       //功能id
 *          funcname:"",    //功能名称
 *          parentid:0,     //父节点id
 *          level:1,        //功能所在层级
 *          orderno:2,      //显示顺序
 *          url:"",         //访问接口url
 *          domkey:""       //dom对象的id
 *      },
 *      children:[
 *          nodedata:{...},
 *          children:[...]
 *      ]
 *   },
 *   {
 *      nodedata:{...},
 *      children:[...]
 *   }
 * ]
 */

 var treenode = {
     //功能树
    rightstree:null,

     /**
      * 将权限树的json字符串加载到树对象上
      * @param {权限树的json字符串} rights 
      */
    loaddata(rights){
        //将缓存的json字符串,转为json对象,为一级树节点的数组
        var treenode = json.parse(rights);        
        return treenode;
    },

    /**
     * 在给定树上,找到上级domkey为superdomkey的给定domkey的树节点
     * 不同子树如果存在子节点domkey重复的情况,也可以区分
     * @param {给定树节点} rightstree 
     * @param {上级的domkey} superdomkey 
     * @param {树节点的domkey} domkey 
     */
    lookupnodebydomkeys(rightstree,superdomkey,domkey){
        var node = null;
        var supernode = null;
        //先寻找superdomkey
        if(superdomkey != ""){
            //如果上级对象的domkey非空
            supernode = this.lookupnodebydomkey(rightstree,superdomkey);
        }
        if (supernode != null){
            //如果上级节点非空,或已找到,则在子树上搜索,可加快搜索速度,并且可避免子节点domkey重复的情况
            node = this.lookupnodebydomkey(supernode,domkey);
        }else{
            node = this.lookupnodebydomkey(rightstree,domkey);
        }
        return node;
    },

    /**
     * 在给定的子树中,搜索指定domkey的树节点
     * @param {子树} rightstree 
     * @param {domkey} domkey 
     */
    lookupnodebydomkey(rightstree,domkey){
        var node = null;
        var functioninfo = rightstree.nodedata;
        //先查找自身的数据
        if (functioninfo.domkey == domkey){
            //如果找到,则返回
            return rightstree;
        }
        //搜索子节点
        for (var i = 0; i < rightstree.children.length; i++){
            var item = rightstree.children[i];
            node = this.lookupnodebydomkey(item,domkey);
            if (node != null){
                break;
            }
        }

        return node;
    }    
 }

 export default treenode;

  如果domkey确保唯一的话,使用map可能是访问效率更高的方案。这里还是使用树型结构来管理权限树。

3.5、创建公共方法模块

  vue项目中,在/src/common目录下,创建commonfuncs.js文件。代码如下:

import treenode from './treenode.js'


var commonfuncs =  {
  checkrights(superdomkey){    
    //先加载权限树
    if (treenode.rightstree == null){
      let rights = localstorage.getitem('rights');
      if (rights === null || rights === ''){
        //没有权限树
        return;
      }
      //加载权限树
      treenode.rightstree = treenode.loaddata(rights);
    }

    //获取class包含permissions的所有dom对象
    var elements = document.getelementsbyclassname('permissions');
    for(var i = 0; i < elements.length; i++){
      var element = elements[i];
      if (element.id != undefined)
      {
        var node = null;
        //如果对象有id,检查权限
        if (superdomkey == null || superdomkey == undefined){
          //如果未指定上级domkey,直接查找
          node = treenode.lookupnodebydomkey(treenode.rightstree,element.id);
        }else{
          //指定上级domkey
          node = treenode.lookupnodebydomkeys(treenode.rightstree,superdomkey,element.id)
        }
        if (node != null && node != undefined){
          //包含节点
          element.style.display = "";
          console.log('has rights :'+element.id);
        }else{
          element.style.display="none";      
          console.log('has not rights :'+element.id);
        }          
      }          
    }
  }
};

export default commonfuncs;

  checkrights方法,参数为superdomkey,即指定上级节点的domkey,允许为空或空串,相当于不指定。其查找当前页面或scoped范围的文档中,class名称包含permissions的所有dom元素。取得dom的id,即功能节点的domkey,如果在权限树中存在对应节点,则表示有权限;否则表示无权限。(注意:前端的权限树都是有权限的功能节点)。

3.6、修改main.js

  修改main.js文件,使得公共模块生效。代码如下:

// the vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import vue from 'vue'
import app from './app'
import router from './router'
import store from './store'
import elementui from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
import md5 from 'js-md5';
import axios from 'axios'
import vueaxios from 'vue-axios'
import treenode_ from './common/treenode.js'
import commonfuncs_ from './common/commonfuncs.js'
import instance_ from './api/index.js'
import global_ from '../config/global.js'

vue.use(vueaxios,axios)
vue.prototype.$md5 = md5
vue.prototype.treenode = treenode_
vue.prototype.$baseurl = process.env.api_root
vue.prototype.instance = instance_  //axios实例
vue.prototype.global = global_
vue.prototype.commonfuncs = commonfuncs_

vue.use(elementui)
vue.config.productiontip = false

/* eslint-disable no-new */
var vue = new vue({
  el: '#app',
  router,
  store,  
  components: { app },
  template: '<app/>',
  render:h=>h(app)    
})

export default vue

  引入了commonfuncs和treenode全局对象,可以在vue文件中使用。

3.7、组件示例

  侧边导航栏,与权限控制相关,可以作为示例。文件为left.vue,代码如下:

<template>
  <div class="left-sidebar">
    <el-menu :default-openeds="['1']" style="background:#f0f6f6;">
      <el-submenu index="1">
        <el-menu-item-group > 
          <el-menu-item index="1-1">
            <router-link class="menu" tag="li" to="/home" exact-active-class="true"
                id="homemenu" active-class="_active">
                <i class="el-icon-s-home"></i>首页
            </router-link>
          </el-menu-item>
          <el-submenu index="1-2" id="usermanagementmain">
            <template slot="title" ><i class="el-icon-user-solid"></i>用户管理</template>
            <el-menu-item index="1-2-1" class="permissions" id="usermanagementsub">
                <router-link class="menu" tag="li" to="/usermanagement">
                  <i class="el-icon-user"></i>用户管理
                </router-link>
            </el-menu-item>
            <el-menu-item index="1-2-2" class="permissions" id="changepassword">
                <router-link class="menu"tag="li" to="/changepassword">
                  <i class="el-icon-key"></i>修改密码
                </router-link>
            </el-menu-item>            
          </el-submenu>  
          <el-menu-item index="1-3" class="permissions" id="questionnairemanagement">
            <router-link class="menu" tag="li" to="/questionnairemanagement">
              <i class="el-icon-document"></i>问卷内容管理
            </router-link>
          </el-menu-item>
          <el-submenu index="1-4" class="permissions" id="issuemanagementmain">
            <template slot="title"><i class="el-icon-message"></i>问卷发布管理</template>
            <el-menu-item index="1-4-1" class="permissions" id="issuemanagementsub">
                <router-link  class="menu" tag="li" to="/issuemanagement">
                  <i class="el-icon-phone"></i>发布问卷查询
                </router-link>
            </el-menu-item>
            <el-menu-item index="1-4-2" class="permissions" id="issuetaskquery">
                <router-link class="menu" tag="li" to="/issuetaskquery">
                  <i class="el-icon-tickets"></i>发布任务查询
                </router-link>
            </el-menu-item>
          </el-submenu>
          <el-menu-item index="1-5" class="permissions" id="answersheetmanagement">
            <router-link class="menu" tag="li" to="/answersheetmanagement">
              <i class="el-icon-receiving"></i>答卷管理
            </router-link>
          </el-menu-item>                   
        </el-menu-item-group>
      </el-submenu>
    </el-menu>
  </div>
</template>

<style>
  /* 去掉右边框 */
  .el-menu {
    border-right: none;
  } 

  .el-submenu {
    background-color: rgb(231, 235, 220) ;
  }  
</style>

  注意那些:class="permissions" id=“xxx”的dom元素,基本都是el-menu-item。这里,将scoped去掉了,因为菜单项,目前只有侧边导航栏在使用。

3.7、修改app.vue

  app.vue,作为应用页面组件的总成,在里面进行总的权限控制。代码如下:

<template>
  <div id="app">
    <!-- 其他页 -->
    <el-container style="min-height: calc(100% - 50px);" v-if="$route.meta.keepalive">
      <!-- 无头部导航栏 -->
      <el-container>
        <el-aside :style="{width:collpasewidth}">
          <!-- 侧边栏 -->
          <keep-alive>
            <left></left>
          </keep-alive>
        </el-aside>
        <el-main>
          <!-- body -->
          <router-view></router-view>
        </el-main>
      </el-container>
      <!-- 无足部 -->
    </el-container>
    
    <!-- 登录页 -->
    <router-view v-if="!$route.meta.keepalive"></router-view>  
  </div>
</template>

<script>
import left from './components/left.vue'

export default {
  name: 'app',
  components: {
    left: left
  },
  data(){
    return {
      collpasewidth:200
    }
  },
  mounted:function(){
    this.commonfuncs.checkrights();
  },
  methods: {
    
  }   
}
</script>

<style>
#app {
  font-family: 'avenir', helvetica, arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

  在页面加载时,调用commonfuncs.checkrights()方法,进行权限控制。

3.8、测试一下

3.8.1、获取权限树数据

  登录成功后,后端输出的权限树数据如下:

{
	rights = {
		"nodedata": {
			"funcid": 0,
			"funcname": "root",
			"parentid": -1,
			"level": 0,
			"orderno": 0,
			"url": "",
			"domkey": ""
		},
		"children": [{
			"nodedata": {
				"funcid": 1,
				"funcname": "用户管理一级菜单",
				"parentid": 0,
				"level": 1,
				"orderno": 0,
				"url": "",
				"domkey": "usermanagementmain"
			},
			"children": [{
				"nodedata": {
					"funcid": 3,
					"funcname": "修改密码",
					"parentid": 1,
					"level": 2,
					"orderno": 1,
					"url": "/userman/changepassword",
					"domkey": "changepassword"
				},
				"children": []
			}]
		}, {
			"nodedata": {
				"funcid": 10,
				"funcname": "问卷内容管理一级菜单",
				"parentid": 0,
				"level": 1,
				"orderno": 1,
				"url": "",
				"domkey": "questionnairemanagement"
			},
			"children": [{
				"nodedata": {
					"funcid": 11,
					"funcname": "新增问卷",
					"parentid": 10,
					"level": 2,
					"orderno": 0,
					"url": "/questionnaireman/addquestionnaire",
					"domkey": "addquestionnaire"
				},
				"children": []
			}, {
				"nodedata": {
					"funcid": 12,
					"funcname": "编辑问卷",
					"parentid": 10,
					"level": 2,
					"orderno": 1,
					"url": "/questionnaireman/editquestionnaire",
					"domkey": "editquestionnaire"
				},
				"children": []
			}, {
				"nodedata": {
					"funcid": 13,
					"funcname": "查询问卷",
					"parentid": 10,
					"level": 2,
					"orderno": 2,
					"url": "/questionnaireman/queryquestionnaires",
					"domkey": "queryquestionnaire"
				},
				"children": []
			}, {
				"nodedata": {
					"funcid": 14,
					"funcname": "复制新建问卷",
					"parentid": 10,
					"level": 2,
					"orderno": 3,
					"url": "",
					"domkey": "copyaddquestionnaire"
				},
				"children": []
			}, {
				"nodedata": {
					"funcid": 15,
					"funcname": "浏览问卷",
					"parentid": 10,
					"level": 2,
					"orderno": 4,
					"url": "/questionnaireman/previewquestionnaire",
					"domkey": "browsequestionnaire"
				},
				"children": []
			}, {
				"nodedata": {
					"funcid": 16,
					"funcname": "提交审核",
					"parentid": 10,
					"level": 2,
					"orderno": 5,
					"url": "/questionnaireman/submitaduit",
					"domkey": "submitaudit"
				},
				"children": []
			}, {
				"nodedata": {
					"funcid": 18,
					"funcname": "作废问卷",
					"parentid": 10,
					"level": 2,
					"orderno": 7,
					"url": "/questionnaireman/cancelquestionnaire",
					"domkey": "cancelquestionnaire"
				},
				"children": []
			}]
		}, {
			"nodedata": {
				"funcid": 20,
				"funcname": "问卷发布管理一级菜单",
				"parentid": 0,
				"level": 1,
				"orderno": 2,
				"url": "",
				"domkey": "issuemanagementmain"
			},
			"children": [{
				"nodedata": {
					"funcid": 21,
					"funcname": "发布管理二级菜单",
					"parentid": 20,
					"level": 2,
					"orderno": 0,
					"url": "",
					"domkey": "issuemanagementsub"
				},
				"children": []
			}, {
				"nodedata": {
					"funcid": 22,
					"funcname": "发布任务查询",
					"parentid": 20,
					"level": 2,
					"orderno": 1,
					"url": "",
					"domkey": "issuetaskquery"
				},
				"children": []
			}]
		}, {
			"nodedata": {
				"funcid": 40,
				"funcname": "答卷管理一级菜单",
				"parentid": 0,
				"level": 1,
				"orderno": 3,
				"url": "",
				"domkey": "answersheetmanagement"
			},
			"children": [{
				"nodedata": {
					"funcid": 41,
					"funcname": "查询答卷记录",
					"parentid": 40,
					"level": 2,
					"orderno": 0,
					"url": "/answersheetman/queryanswertask",
					"domkey": "queryanswersheet"
				},
				"children": []
			}, {
				"nodedata": {
					"funcid": 42,
					"funcname": "回收记录明细",
					"parentid": 40,
					"level": 2,
					"orderno": 1,
					"url": "/answersheetman/getanswersubmitdetail",
					"domkey": "recoverydetail"
				},
				"children": []
			}, {
				"nodedata": {
					"funcid": 43,
					"funcname": "答卷统计",
					"parentid": 40,
					"level": 2,
					"orderno": 2,
					"url": "/answersheetman/querystatresult",
					"domkey": "answersheetstat"
				},
				"children": []
			}, {
				"nodedata": {
					"funcid": 44,
					"funcname": "答卷原始记录",
					"parentid": 40,
					"level": 2,
					"orderno": 3,
					"url": "/answersheetman/queryoriginalanswer",
					"domkey": "queryoriginalanswer"
				},
				"children": []
			}]
		}]
	}, token = 873820ba39e64005bcce3e54a830ab2c
}

  这些功能项中,有些与导航栏有关,还有一些是页面的按钮或链接,在示例中没有用到。

3.8.2、制作首页

  制作一个简单的首页home.vue,代码如下:

<template>
  <div id="home">
    <h4>欢迎使用</h4>
    <h3>xx系统</h3>
  </div>
</template>

3.8.3、简单设置路由导航文件

  修改/src/router/index.js文件,代码如下:

import vue from 'vue'
import router from 'vue-router'
import helloworld from '@/components/helloworld'
import home from '@/components/home.vue'
import login from '@/components/login/login.vue'

vue.use(router)

const router = new router({
  routes: [
    {
      path: '/home',
      name: 'home',
      component: home,
      meta: {
        keepalive: true
       }         
    },
    {
      path: '/login',
      name: 'login',
      component: login,
      meta: {
        keepalive: false
       }      
    },    
  ]
})

// 导航守卫
// 使用 router.beforeeach 注册一个全局前置守卫,判断用户是否登陆
router.beforeeach((to, from, next) => {
  if (to.path === '/login') {
    next();
  } else {
    let token = localstorage.getitem('token');
 
    if (token === null || token === '') {
      next('/login');
    } else {  
      if (to.path === '/'){
        next('/home');
      }else{
        next();
      }
    }
  }
});

export default router;

3.8.4、导航栏效果测试

  现在运行vue,"npm run dev",然后显示首页,并用f12显示调式信息:

  侧边栏页面显示如下:

Vue前端访问控制方案

  浏览器的调试器的控制台输出信息为:

Vue前端访问控制方案

  说明,domkey为usermangementsub的dom元素没有操作权限,与侧边栏的效果一致。

3.8.5、scoped情况测试

  login.vue,使用了scoped,作为示例,现在将登录按钮,进行权限控制,修改如下:

      <el-form-item>
        <el-button type="primary" class="permissions" id="login" style="width:160px" @click="submitform('form')">登录</el-button>
      </el-form-item>

  在login.vue的script的mounted方法中,增加权限控制代码:

  mounted:function(){
    //页面加载时,显示验证码
    this.getverifycode();
    this.commonfuncs.checkrights();
  },

  由于domkey为login的,没有在权限树中,故其加入权限控制集合,又没有被授权,则该按钮应该不可见。

  运行测试,显示登录页,效果图如下:

Vue前端访问控制方案

  登录按钮不可见了,与预期效果一致。

3.9、登录成功后保存信息

  登录成功后,将后端发生过来的token和权限树保存起来,并将json字符串转为json对象。

  代码如下:

   submitform(formname) { 
      let _this = this;     
      this.$refs[formname].validate(valid => {
        // 验证通过为true,有一个不通过就是false
        if (valid) {
          // 通过的逻辑
          let passwd = this.$md5(this.form.password);
          this.instance.userlogin(this.$baseurl,{
              loginname:_this.form.username,
              password:passwd,
              verifycode:_this.form.verifycode
          }).then(res => {
            console.log(res.data);
            if (res.data.code == this.global.sucessrequstcode){
              //如果登录成功
              _this.usertoken = res.data.data.token;
              _this.rights = res.data.data.rights;
              //更新权限树
              this.treenode.rightstee = this.treenode.loaddata(_this.rights);
              console.log(this.treenode.rightstee)
              // 将用户token和权限树保存到vuex中
              _this.changelogin({ token: _this.usertoken, rights: _this.rights});
              _this.$router.push('/home');
              //alert('登陆成功');
            }else{
              alert(res.data.message);
            }
          }).catch(error => {
            alert('账号或密码错误');            
            console.log(error);
          });
        } else {
          console.log('验证失败');
          return false;
        }
      });
    },

3.10、权限动态更新的拦截处理

  根据权限动态更新方案,管理员修改用户权限后,该用户第一次访问后端接口,返回信息中可能会携带附加信息。这个可能在任何返回json格式数据的接口中发生。因此,可使用拦截器,来进行统一处理。

import axios from 'axios';
import router from '../router'
import vue from 'vue';
import vuex from 'vuex';
import treenode from '../common/treenode.js'

const instance = axios.create({
  timeout: 60000,
   headers: {
    'content-type': "application/json;charset=utf-8"
  }
});

//token相关的response拦截器
instance.interceptors.response.use(response => {  
  if (response) {
    switch (response.data.code) {
      case 3: //token为空
      case 4: //token过期
      case 5: //token不正确    
        localstorage.clear();     //删除用户信息
        //要跳转登陆页
        alert('token失效,请重新登录!');
        router.replace({
              path: '/login',
        });
        break;
      default:
        break;
    }
    if(response.data.additional){
      //如果包含附加信息
      var data = {};
      if(response.data.additional.token){
        //如果包含token
        data.token = response.data.additional.token;
        localstorage.setitem('token', data.token);
      }
      if(response.data.additional.rights) {
        data.rights = response.data.additional.rights;
        localstorage.setitem('rights', data.rights);
        //刷新权限树
        treenode.rightstree = treenode.loaddata(data.rights);
      }
    }
  }
  return response;
}, error => {
  return promise.reject(error.response.data.message) //返回接口返回的错误信息
})