zoukankan      html  css  js  c++  java
  • Vue前端访问控制方案

    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="{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显示调式信息:

      侧边栏页面显示如下:

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

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

    3.8.5、scoped情况测试

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

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

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

      mounted:function(){
        //页面加载时,显示验证码
        this.getVerifyCode();
        this.commonFuncs.checkRights();
      },
    

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

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

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

    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) //返回接口返回的错误信息
    })
    
    作者:阿拉伯1999
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
    养成良好习惯,好文章随手顶一下。
  • 相关阅读:
    科学计算和可视化
    利用Python制作GIF图片
    模拟体育竞技分析
    词云(傲慢与偏见)
    词频统计+词云(傲慢与偏见)
    汉诺塔问题
    Python 的turtle笔记
    有进度条的圆周率计算
    Python 第二周练习
    warning: deprecated conversion from string constant to ‘char*’
  • 原文地址:https://www.cnblogs.com/alabo1999/p/14960584.html
Copyright © 2011-2022 走看看