vue实现菜单权限控制的示例代码

大家在做后台管理系统时一般都会涉及到菜单的权限控制问题。当然解决问题的方法无非两种——前端控制和后端控制。我们公司这边的产品迭代速度较快,所以我们是从前端控制路由迭代到后端控制路由。下面我会分别介绍这两种方法的优缺点以及如何实现(不熟悉vue-router API的同学可以先去官网看一波API哈)。

我先简单说下项目的需求:如下图所示,有一级菜单和二级菜单,然后不同的人登录进去会展示不同的菜单。

前端控制路由的思路:将所有的路由映射表都拿到前端来维护,就是我的router.js里面将所有的菜单path与对应的components全部写进去,后面我会提到全部写进去的弊端。然后我的左侧菜单写成一个组件(sidebar.vue),在这个组件里写一份类似这样的data数据,然后通过登录时获取的level值来给data中固定的菜单加hidden,然后前端就根据hidden展示菜单。

// router.js 伪代码
const Login = r => require.ensure([],()=>r(require('../page/login/Login.vue')),'login');
const Home = r => require.ensure([],()=>r(require('../page/Home.vue')),'home');
const Forbidden = r => require.ensure([],()=>r(require('../page/403.vue')),'forbidden');
const NotFound = r => require.ensure([],()=>r(require('../page/404.vue')),'notfound');
const Dashboard = r => require.ensure([],()=>r(require('../page/dashboard/Dashboard.vue')),'dashboard');
const SplashScreen = r => require.ensure([],()=>r(require('../page/splashScreen/SplashScreen.vue')),'splashScreen');
const AddSplashScreen = r => require.ensure([],()=>r(require('../page/splashScreen/AddSplashScreen.vue')),'addSplashScreen');

const routes = [
  {
    path: '/',
    redirect: '/login'
  },{
    path: '/login',
    component: Login
  },{
    path: '/404',
    component: NotFound
  },{
    path: '/home',
    component: Home,
    redirect: '/home/splashScreen',
    children: [
      {
        path: '/home/splashScreen',
        component: SplashScreen,
        meta: {
          title: '国服李白
        }
      },{
        path: '/home/addSplashScreen',
        component: AddSplashScreen,
        meta: {
          title: 国服吕布'
        }
      }
    ]
  }
];

下面是菜单组件的伪代码

// sidebar.vue
<template>
  <div class="sidebar">
    <el-menu>
      ...
    </el-menu>
  </div>
</template>
<script>
  export default {
    data() {
      return {
        routes: [
          {
           index: '1',
           title: '国服打野',
           icon: 'iconfont icon-guanggao',
           children: [
            {
             index: 'splashScreen',
             title: '李白',
             children: []
            },
           ]
          },
          {
            index: '2',
            title: '国服中单',
            icon:'iconfont icon-tuisongguanli-',
          }
        ]
      }
    },
    methods: {
      getLevel(){
        const level = sessionStorage.getItem('level');
        if(level === '0'){
          this.routes.forEach(function(value){
            if(value.title == "国服上单"){
              value.hidden = true;
              value.children.forEach(function(value){
                if(value.title=="国服关羽"){
                  value.hidden = true;
                }
              })
            }
          })
        }else if(level === '1'){
          this.routes.forEach(function(value){
            value.hidden = true
            value.children.forEach(function(value){
              value.hidden = true;
            })
          })
        }
      }
    },
    created(){
      this.getLevel();
    }
  }
</script>

虽然说这样可以实现权限功能,但有两个问题。

  1. session里存的是level,我们可以打开浏览器控制台人为控制level,这样就失去了权限的意义。
  2. 我们如果记住了path,可以直接在浏览器网址栏中手动输入path,然后回车就可以看到任何页面。这也是前端router.js写死所有路由的弊端。

在这里面前端只是通过后端传回的level来给router显示/隐藏,这样前端维护整个路由是比较复杂的而且是有重大隐患的。

现在呢我们来讲讲后端控制路由。先从操作流程来说,我们这边加入了一个dashboard中间页,这个页面只展示不同level下的一级路由,通过点击相应的一级路由进到对应的Page页面,该page页面也只展示相对应的所有的二级路由。

这里面出现了两个个新的概念叫 “动态添加路由”和“导航守卫”,就是我前端router.js中只写所有人可以访问的路由表,比如login和404页面等。其他所有的组件资源全部写到一个新的components.js文件中,然后通过后端返回的menuData去映射符合components.js中的key,如果有对应的,就把它动态添加到router中,通过addRoutes添加。动态添加路由这个方法要写到导航守卫beforeEach这个钩子函数中。

导航守卫的意思是我路由跳转到下个页面之前要做些什么。就是说我们登录后会跳到dashboard页面,在进到这个页面之前我们需要将后端请求回来的menuData进行二次封装,把他根据权限返回回来的data与我们前端components.js去做map匹配,将最终的数据通过addRoutes来push到我们的路由中,之后才能进到我们的dashborad页面,再通过dashborad页面进到对应的page页面,就是说我们把所有的权限控制全在dashboard页面进入之前就做完了。

这里面还有一个小的优化的点:当我们通过前面说的浏览器菜单栏访问到非权限页面或者不存在的页面时,需要根据vue-router中的匹配优先级来最后addRoutes 404和*这个页面,这样就可以直接到达404页面而非空页面。

// components.js 所有的页面资源
const home = () => import('../page/Home.vue');
const splashScreen = () => import('../page/splashScreen/SplashScreen.vue');
const addSplashScreen = () => import('../page/splashScreen/AddSplashScreen.vue');
const editSplashScreen = () => import('../page/splashScreen/EditSplashScreen.vue');

export default {
  home,
  splashScreen,
  addSplashScreen,
  editSplashScreen,

};
// router.js 看,只写通用的页面是不是很清爽
import Vue from 'vue';
import Router from 'vue-router';

Vue.use(Router);

const Login = () => import('../page/login/Login.vue');
const Home = () => import('../page/Home.vue');
const Forbidden = () => import('../page/403.vue');
const Dashboard = () => import('../page/dashboard/Dashboard.vue');
const routes = [
  {
    path: '/',
    redirect: '/login'
  },{
    path: '/login',
    component: Login
  },{
    path: '/403',
    component: Forbidden
  },
  {
    path: '/dashboard',
    component: Dashboard,
  },
];
export default new Router({
  mode: 'history',
  routes: routes,
  base: __dirname,
  linkActiveClass: 'link-active'

})
// main.js 伪代码 只保留具体的相关逻辑
import routeMap from './router/component.js';
const NotFound = () => import('./page/404.vue');
const formatRoutes = function (routes, routeData) {
  if (!routeData) {
    routeData = {
      name: 'home',
      path: '/home',
      // 组件匹配成功的话才可以访问具体的页面
      component: routeMap['home'],
      children: [],
    };
  }
  routes.length && routes.forEach(route => {
    if(route.component) {
      route.component = routeMap[route.component];
      routeData.children.push({
        path: route.path,
        name: route.index,
        component: route.component,
        meta: {
          title: route.title,
        },
      })
    }
    if (route.children && route.children.length) {
      formatRoutes(route.children, routeData);
    }
  });
  return routeData;
};

let isFetchRemote = true;

//使用钩子函数对路由进行权限跳转
router.beforeEach((to, from, next) => {
  const username = sessionStorage.getItem('username');
  if(!username && to.path !== '/login'){
    next({path: '/login'});
  }
  else if (isFetchRemote && to.path !== '/login') {
    ajaxPost('/resourceAPI/getMenuData').then(res =>{
      if (res.status === 200 && res.data.errno === 0) {
        isFetchRemote = false;
        const menuData = res.data.result;
        localStorage.setItem('menudata', JSON.stringify(menuData));
        const routeData = formatRoutes(menuData);
        resourceApp.$router.addRoutes([routeData].concat([
          {name:'404',path:'/404',component:NotFound},
          {path:'*',redirect: '/404'}]));
        resourceApp.$router.push({
          path: to.path,
          query: to.query
        });
      }
      else {
        isFetchRemote = true;
      }
      next();
    })
    .catch(err => {
      console.log(err);
    });
  }
  else {
    next();
  }
});

const resourceApp = new Vue({
  router,
  render: h => h(App)
}).$mount('#app');
// menuData请求数据
// 一级菜单与二级菜单的区别是一级菜单带有component这个值,比如下面的短信管理就是只有一级菜单
{
  "errno": 0,
  "errmsg": "获取权限成功",
  "result": [
    {
      "index": "1",
      "title": "打野位置",
      "icon": "iconfont icon-guanggao",
      "children": [
        {
          "index": "splashScreen",
          "icon": "",
          "title": "娜可露露",
          "path": "/home/splashAdverse",
          "component": "splashAdverse",
          "isShow": true
        },
        {
          "index": "addSplashScreen",
          "icon": "",
          "title": "李白",
          "path": "/home/addAdverse",
          "component": "addAdverse",
          "isShow": false
        }, 

      ]
    },
    {
      "index": "message",
      "title": "国服上单",
      "icon": "iconfont icon-duanxinguanli",
      "path": "/home/message",
      "component": "message",
      "children": [
        {
          "index": "addMessage",
          "title": "国服第一关羽",
          "icon": "",
          "path": "/home/addMessage",
          "component": "addMessage",
          "isShow": false
        }

      ]
    }
  ]
}

而sidebar和dashboard这两个组件都只需要通过session拿到后端的menudate就可以。

// dashboard 伪代码
<template>
  <div class="nav_list">
    <div class="nav_list_item" v-for="item in navList" @click="goPage(item)">
      <i :class="item.icon"></i>
      <h2>{{item.title}}</h2>
    </div>
  </div>
</template>

<script>
  created(){
    const routeArr = JSON.parse(localStorage.getItem('menudata'));
    this.navList = routeArr;
  },
  methods: {
    goPage(item){
      // 只有一级菜单
      if(item.component){
        this.$router.push(item.path);
      }else{
        // 二级菜单的数据结构中只在children中有path
        this.$router.push(item.children[0]['path']);
      }
    }
  }
</script>
// sidebar 伪代码
<script>
  export default {
    data() {
      return {
        routes: [],
      }
    },
    methods: {
      bouncer(arr){
        return arr.filter(function(val){
         return !(!val || val === "");
        });
      }
    },
    created(){
      const menuData = JSON.parse(localStorage.getItem('menudata'));
      // 通过当前router的path来map对应的整个路由数组
      let routes = menuData.map((item)=>{

        // 只有一级路由
        if(item.component && item.path == this.$route.path){
          console.log(item)
          return item;
        }else{
          if(item.children[0]['path'] == this.$route.path){
            console.log(item)
            return item;
          }
        }
      })
      // 去掉数组中的undefined、null 等空值 假值
      this.routes = this.bouncer(routes);
    }
  }
</script>

通过这种方式来控制权限,我们如果在浏览器控制台改了session中的level或者在浏览器导航栏改path,都会回归到导航守卫中,就是发请求重新获取menuData,当我addRoutes后如果没有匹配到这个值就回到404,当然通过改level也不会达到修改权限的控制,因为我们是动态获取路由,不是之前的前端控制路由。

到此这篇关于vue实现菜单权限控制的示例代码的文章就介绍到这了,更多相关vue菜单权限内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • vue addRoutes实现动态权限路由菜单的示例

    需求 最近接手一个后台管理系统,需要实现导航菜单从后台拉取的效果:根据登录用户的权限不同分别拉出来的导航菜单也不一样,另外可操作的界面也存在区别. 问题 因为后台管理系统是准备使用vue+vue-router+element-ui+vuex的搭配来做的,可是单页应用在进入页面之前就已经将vue-router实例化并且注入vue实例中了,所以在进入登录页面的时候旧没办法在重新定制路由了.接下来各种百之谷之,发现vue-router在2.0版本中提供了addRoutes方法添加路由,希望的曙光出现.

  • vue实现后台管理权限系统及顶栏三级菜单显示功能

    •效果演示地址 项目demo展示 重要功能总结 权限功能的实现 权限路由思路: 根据用户登录的roles信息与路由中配置的roles信息进行比较过滤,生成可以访问的路由表,并通过router.addRoutes(store.getters.addRouters)动态添加可访问权限路由表,从而实现左侧和顶栏菜单的展示. 实现步骤: 1.在router/index.js中,给相应的菜单设置默认的roles信息: 如下:给"权限设置"菜单设置的权限为:meta:{roles: ['admin

  • vue实现菜单权限控制的示例代码

    大家在做后台管理系统时一般都会涉及到菜单的权限控制问题.当然解决问题的方法无非两种--前端控制和后端控制.我们公司这边的产品迭代速度较快,所以我们是从前端控制路由迭代到后端控制路由.下面我会分别介绍这两种方法的优缺点以及如何实现(不熟悉vue-router API的同学可以先去官网看一波API哈). 我先简单说下项目的需求:如下图所示,有一级菜单和二级菜单,然后不同的人登录进去会展示不同的菜单. 前端控制路由的思路:将所有的路由映射表都拿到前端来维护,就是我的router.js里面将所有的菜单p

  • Vue 实现前端权限控制的示例代码

    登录&&权限流程图 前言 首先我们确定的权限控制分为三大部分,其中根据粒度大小分的更细: 登录权限控制 页面权限控制 菜单中的页面是否可以被访问 页面中的按钮 (增.删.改.查)的权限控制是否显示 接口权限控制 一.登录权限控制 登录访问权限控制是对用户的校验.在用户登录成功之后,后台将返回一个token,之后前端每次进行接口请求的时候,都要带上这个token.后台拿到这个token后进行判断,如果此token确实存在并且没有过期,则可以通过访问.如果token不存在或后台判断已过期,则会

  • Springboot+Vue+shiro实现前后端分离、权限控制的示例代码

    本文总结自实习中对项目的重构.原先项目采用Springboot+freemarker模版,开发过程中觉得前端逻辑写的实在恶心,后端Controller层还必须返回Freemarker模版的ModelAndView,逐渐有了前后端分离的想法,由于之前,没有接触过,主要参考的还是网上的一些博客教程等,初步完成了前后端分离,在此记录以备查阅. 一.前后端分离思想 前端从后端剥离,形成一个前端工程,前端只利用Json来和后端进行交互,后端不返回页面,只返回Json数据.前后端之间完全通过public A

  • Springboot+Spring Security实现前后端分离登录认证及权限控制的示例代码

    目录 前言 本文主要的功能 一.准备工作 1.统一错误码枚举 2.统一json返回体 3.返回体构造工具 4.pom 5.配置文件 二.数据库表设计 初始化表数据语句 三.Spring Security核心配置:WebSecurityConfig 四.用户登录认证逻辑:UserDetailsService 1.创建自定义UserDetailsService 2.准备service和dao层方法 五.用户密码加密 六.屏蔽Spring Security默认重定向登录页面以实现前后端分离功能 1.实

  • Spring Security实现统一登录与权限控制的示例代码

    目录 项目介绍 统一认证中心 配置授权服务器 配置WebSecurity 登录 菜单 鉴权 资源访问的一些配置 有用的文档 项目介绍 最开始是一个单体应用,所有功能模块都写在一个项目里,后来觉得项目越来越大,于是决定把一些功能拆分出去,形成一个一个独立的微服务,于是就有个问题了,登录.退出.权限控制这些东西怎么办呢?总不能每个服务都复制一套吧,最好的方式是将认证与鉴权也单独抽离出来作为公共的服务,业务系统只专心做业务接口开发即可,完全不用理会权限这些与之不相关的东西了.于是,便有了下面的架构图:

  • 基于Vue实现后台系统权限控制的示例代码

    用Vue/React这类双向绑定框架做后台系统再适合不过,后台系统相比普通前端项目除了数据交互更频繁以外,还有一个特别的需求就是对用户的权限控制,那么如何在一个Vue应用中实现权限控制呢?下面是我的一点经验. 权限控制是什么 在权限的世界里服务端提供的一切都是资源,资源可以由请求方法+请求地址来描述,权限是对特定资源的访问许可,所谓权限控制,也就是确保用户只能访问到被分配的资源.具体的说,前端对资源的访问通常是由界面上的按钮发起,比如删除某条数据:或由用户进入某一个页面发起,比如获取某个列表数据

  • vue-router 权限控制的示例代码

    最近搭建了公司的后台管理系统, 而且系统还比较庞大, 要实现以下几点: 菜单权限, 根据不同权限显示不同的菜单 操作权限, 比如有些账号没有新增权限, 有些没有修改或者删除权限 数据权限, 比如统计概况, 普通管理员不能看到公司营业概况,但能看到自己所属区域的概况 显示权限, 比如列表, 运营能看到那一列的签约金额,但市场不能看到签约金额这一列 到目前为止还在构建中, 已经解决菜单权限和操作权限. 菜单权限: 最开始的时候也是按照楼上给出的方法,本地先配置一套路由,然后登陆成功后会从服务器返回一

  • 关于Vue的路由权限管理的示例代码

    前言 曾经在工作上对 vue 路由权限管理这方面有过研究,这几天又看到了几篇相关的文章,再加上昨天电面中又再一次提及到,就索性整理了一下自己的一些看法,希望对大家有帮助. 实现 大体上实现的思路很简单,先上图: 无非是将路由配置按用户类型分割为 用户路由 和 基本路由,不同的用户类型可能存在不同的 用户路由,具体依赖实际业务. 用户路由: 当前用户所特有的路由 基本路由:所有用户均可以访问的路由 实现控制的方式分两种: 通过vue-router addRoutes方法注入路由实现控制 通过vue

  • Vue 简单实现前端权限控制的示例

    简要说明 最近写了一下vue控制权限(菜单.路由)的项目,用了vuex.addRoutes动态添加路由方法等,总共100多行代码,跟大家分享一下~ 逻辑梳理 除登录接口.退出接口外,其余接口增加token验证. 打开页面时请求获取菜单接口,请求不成功说明未登录,给route默认添加login页面以及 * 重定向. 登录成功后获取到token,把token存入session以及请求头. 登录成功后获取菜单接口,请求回来的路由和vuex里面全部的路由进行匹配,获取component. 把获取完com

  • SpringBoot中整合Shiro实现权限管理的示例代码

    之前在 SSM 项目中使用过 shiro,发现 shiro 的权限管理做的真不错,但是在 SSM 项目中的配置太繁杂了,于是这次在 SpringBoot 中使用了 shiro,下面一起看看吧 一.简介 Apache Shiro是一个强大且易用的Java安全框架,执行身份验证.授权.密码和会话管理.使用Shiro的易于理解的API,您可以快速.轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序. 三个核心组件: 1.Subject 即"当前操作用户".但是,在 Shi

随机推荐