阅读 1885

前端权限控制系统的实现思路

一、项目主体

  • 管理平台(代称为ManagePlatform、MP)
  • 一级菜单(代称M)
  • 二级菜单(代称m)

结构图:

二、应用场景

不同用户拥有不同的菜单权限:

  • 管理员(manageUser)拥有可见全部菜单的权限

  • 用户1(user1)拥有可见M1下的全部菜单的权限

  • 用户2(user2)拥有可见M1和M2下的全部菜单的权限

  • 用户3(user3)拥有可见M1、M2下全部及M3的m31菜单权限

三、实现思路

权限控制:

  1. 导航栏菜单是否可见
  2. 校验url能否跳转

服务端:

  1. 接口返回当前用户的权限数据,并实际菜单结构保持一致(MP中包含M1、M2、M3,M1中包含m11、m12,...)
  2. 其中每个菜单包含id(菜单id)、name(菜单名称)、children(子集菜单)等必要属性

前端:

  1. 路由默认配置全部的菜单,但均为隐藏状态(登录和首页无权限,无需限制)
  2. 通过接口获取到该用户的权限数据,并与默认路由进行比对,将对应菜单部分设置为可显示(有权限)
  3. 将菜单所有id存储到本地list,在路由跳转之前校验此路由的id是否包含在list中,如果有,则执行跳转,否则进入到首页(表示没有权限)

四、代码模拟

默认路由routes:

export default [
  {
    path: '/M1',
    redirect: '/M1/m11',
    component: Layout,
    name: 'M1菜单',
    meta: {
      extraMenuId: 1,
      hidden: true
    },
    children: [{
      path: '/M1/m11',
      name: 'm11菜单',
      component(resolve) {
        require(['@views/M1/m11'], resolve);
      },
      meta: {
        extraMenuId: 2,
        hidden: true,
      }
    }, {
      path: '/M1/m12',
      name: 'm12菜单',
      component(resolve) {
        require(['@views/M1/m12'], resolve);
      },
      meta: {
        extraMenuId: 3,
        hidden: true,
      }
    }]
  },
  {
    path: '/M2',
    redirect: '/M2/m21',
    component: Layout,
    name: 'M2菜单',
    meta: {
      extraMenuId: 4,
      hidden: true
    },
    children: [{
      path: '/M2/m11',
      name: 'm21菜单',
      component(resolve) {
        require(['@views/M2/m21'], resolve);
      },
      meta: {
        extraMenuId: 5,
        hidden: true,
      }
    }, {
      path: '/M2/m22',
      name: 'm22菜单',
      component(resolve) {
        require(['@views/M2/m22'], resolve);
      },
      meta: {
        extraMenuId: 6,
        hidden: true,
      }
    }]
  },
  {
    path: '/M3',
    redirect: '/M3/m31',
    component: Layout,
    name: 'M3菜单',
    meta: {
      extraMenuId: 7,
      hidden: true
    },
    children: [{
      path: '/M3/m31',
      name: 'm31菜单',
      component(resolve) {
        require(['@views/M3/m31'], resolve);
      },
      meta: {
        extraMenuId: 8,
        hidden: true,
      }
    }, {
      path: '/M3/m32',
      name: 'm32菜单',
      component(resolve) {
        require(['@views/M3/m32'], resolve);
      },
      meta: {
        extraMenuId: 9,
        hidden: true,
      }
    }, {
      path: '/M3/m33',
      name: 'm33菜单',
      component(resolve) {
        require(['@views/M3/m33'], resolve);
      },
      meta: {
        extraMenuId: 9,
        hidden: true,
      }
    }]
  },
  {
    path: '/login',
    name: '登录',
    component(resolve) {
      require(['@views/login'], resolve);
    },
    meta: {
      hidden: true
    }
  },
  {
    path: '/home',
    name: '首页',
    component(resolve) {
      require(['@views/home'], resolve);
    },
    meta: {
      hidden: true
    }
  },
  {
    path: '*',
    redirect: '/home',
    meta: {
      hidden: true
    }
  }
]
复制代码

菜单id数据menuIdList与路由routes匹配:

// 因管理端菜单展示只展示两层,故只需遍历前两层路由
routes.forEach((item) => {
  if (menuIdList.indexOf(item.meta.extraMenuId) >= 0) {
    item.meta.hidden = false;
    if (item.children && item.children.length) {
      item.children.forEach((child) => {
        if (menuIdList.indexOf(child.meta.extraMenuId) >= 0) {
          child.meta.hidden = false;
        }
      })
    }
  }
});
复制代码

公共方法判断id是否存在:

/**
 * 判断menuId是否存在与Localtorage中
 * @param menuId
 */
export function getMenuAuth(menuId) {
  const menuIdList = JSON.parse(getMenuIdList());
  return menuIdList.indexOf(menuId) >= 0;
}
复制代码

路由守卫校验:

router.beforeEach((to, from, next) => {
  // 判断是否已登录,未登录则进入登录页面
  if (getToken()) {
    if (to.path === '/login') {
      next();
    } else {
      if (to.path === '/' || to.path === '/home') {
        next();
        return;
      }
      // url路由权限校验,没有权限则进入home页面
      if (getMenuAuth(to.meta.extraMenuId)) {
        next();
      } else {
        next('/home');
      }
    }
  } else {
    next('/login');
  }
})
复制代码

以上仅为项目开发过程中的个人思路,如有更好的想法思路请在评论区留言,谢谢!

文章分类
前端
文章标签