人嘛总会有很多借口去拖延一件事,我发现这种人真的是无敌的用那个词怎么讲?哦对就是神烦。你看看我一天天的距离上次更新博客很长时间了吧?一直在忙忙周一到周五不是在上班就是在上班路上哈哈哈,反正就是没有一天在闲着,然后就是嘛.....,hahhahh 就拖着 就那么硬拖,我不是也没找理由 哈哈哈哈,貌似上边已经找了,反正吧,爱咋咋吧你能把我咋着吧!不好意思,先容我跪好哈哈哈。还好我足智多谋,看了个电视剧写了个观后感,灵机一动直接更新到博客,不能只看技术不看精神世界是吧?嗯!可以的!理由都衔接的这么完美。话说最近记忆力不太好,睡不着觉的时候,我想数数羊吧,数着数着,突然一个小羊瞥了我一眼并且非常不屑的说:“你已经数过我一次了”,我天我真的是居然被一只羊嘲笑了。哎人生啊,真的是 “卧槽,无情!”。好了最近没有更新多说了一点还是言归正传吧,上回书说到使用mongodb + vue完成登陆,这次我们要来搞一下后台的页面权限控制。来吧展示。
-
整体思路
-
公共页面不需要用户登陆的路由肯定有一个静态路由表 constantRouterMap
-
在前端肯定有一个带权限路由表 asyncRouterMap
-
后端有一整套的动态路由表存储
-
登陆时获取到用户的用户角色
-
使用Vue Router 路由守卫进行是否有权限进入的或者说存在权限菜单项的判断
-
发送用户角色到后台,返回可访问的路由相关信息,和前端存储的路由表进行匹配
-
使用router.addRoutes 方法进行动态路由的添加
-
思路图
-
-
具体代码
-
Vue前端逻辑源码
-
路由守卫,守的就是底线
//手动跳转的页面白名单 const whiteList = [ '/', '/home', '404' ]; const constantRouterMap = [ { path: "/home", name: "Home", component: Home, }, { path: "/login", name: "Login", component: Login, } ]; const router = new VueRouter({ mode: "history", base: process.env.BASE_URL, routes:constantRouterMap }); router.beforeEach(async (to, from, next) => { if (Cookie.getCookie('username')) { // 验证是否已经登陆过了 if (to.path!=='/login') { if (store.state.menuList.length!==0) { next(); }else{ store.dispatch('getPermission').then(() => { store.dispatch('getPermissionList').then((res)=>{ // .....进行路由合并 { // 此处需特别注意置于最底部 path: "/404", name: "notFound", component: () => import('@/components/layout/404.vue') }, { path: "*", // 此处需特别注意置于最底部 redirect: "/404" //无匹配到的路径自动重定向到404页面 } ] router.addRoutes(accessRoutes) // 动态添加可访问路由表 next({ ...to, replace: true }) }); }).catch((err) => { console.log(err); router.replace('/login') }) } }else{ alert('已经登陆过了'); // 跳转默认的页面 } } else { if (whiteList.indexOf(to.path) !== -1) { // 免登陆白名单 直接进入 next(); } else { if (to.path !== '/login') { // 重定向到登录页面 不能这么写 因为假如之前的角色是 管理员页面 后又登陆了非管理员 重定向的页面就可能不存在,就会导致404 next(`/home?redirect=${to.path}`) //next('/login'); } else { next(); } } } }) export default router;
-
Vuex
/** * 把后台返回的的路由和前端路由表中的路由进行 * @param {*} routes * @param {*} roles */ export function recursionRouter(userRouter, allRouter ) { var realRoutes = [] allRouter.forEach((v) => { userRouter.forEach((item) => { if (item.name === v.name) { if (item.children && item.children.length > 0) { v.children = recursionRouter(item.children, v.children) } realRoutes.push(v) } }) }) return realRoutes } const token = JSON.parse(localStorage.getItem('USER_TOKEN')) || '' // 用户信息 const userRoles = JSON.parse(localStorage.getItem('USER_ROLE'))|| '' // 用户角色 export default new Vuex.Store({ state: { token, userRoles, menuList:[] }, strict:true, // 使用严格模式 mutations: { setUserToken(state,token){ //alert(token); state.token= token; }, SaveLoginInfo(state,userRoles){ state.userRoles = userRoles }, setPermissList(state,data){ state.menuList = data; } }, actions: { getPermission({commit,state}){ return new Promise((resolve,reject)=>{ api.GetPermission({ userRole:state.userRoles }).then(res=>{ commit('setPermissList',res.permission); resolve(res.permission); }).catch((err)=>{ reject(err) }) }) }, getPermissionList({ state }) { return new Promise((resolve) => { let permissionList = [] permissionList = recursionRouter(state.menuList, asyncRouterMap); resolve(permissionList) }) } }, modules: {} });
-
根据路由生成的菜单
<template> <div> <el-aside width="201px" height="100vh" class="app-side app-side-left" :class="isCollapse ? 'app-side-collapsed' : 'app-side-expanded'" > <div class="app-side-logo"> <img src="@/assets/logo.png" :width="isCollapse ? '60' : '60'" height="60" /> </div> <div> <!-- 我是样例菜单 --> <el-menu style="height:calc(100vh - 62px);overflow-y:auto; min-width:201px" :default-openeds= "['0','1']" class="el-menu-vertical-demo" :router="true" :default-active="this.$route.path" @open="handleOpen" @close="handleClose" > <el-submenu v-for="(item,index) in activeMenuList" :key="index" :index="index+''"> <template slot="title" > <i :class="item.type" ></i> <span slot="title">{{item.text}}</span> </template> <div v-for="(c,cindex) in item.children" :key="cindex" > <el-menu-item :index="'/user/'+item.path+ '/'+ c.path" >{{c.text}}</el-menu-item> </div> </el-submenu> </el-menu> </div> </el-aside> </div> </template> <script> //import asyncRouterMap from './../../router/asyncRouterMap' export default { data() { return { isCollapse: true, menuItemData:[], openList:[], }; }, methods: { handleOpen(key, keyPath) { console.log(key, keyPath); }, handleClose(key, keyPath) { console.log(key, keyPath); } }, mounted (){ this.menuItemData = this.$store.state.menuList; }, computed:{ activeMenuList:function(){ return this.menuItemData.filter(function(item){ return item.children.length&&item.children }) } } }; </script>
-
-
mongodb逻辑源码
-
主要是根据用户的角色返回对应角色的路由信息
router.post('/getpermission',async function(ctx,next){ let {userRole} = ctx.request.body; if (!userRole) return ctx.body = {code:4020,msg:'该用户没有任何的权限'}; let args = {userRole:userRole}; const userRouters = await getRouters.query(args); //console.log(userRouters) ctx.body = (userRouters.code ===200) ? {code:200,msg:"获取权限成功",permission:userRouters.routerList} : userRouters })
-
Models 模块下的父路由和子路由 (这里踩了一个坑,当时认为mongodb的表直接把父路由和子路由直接写在一个表里,这样造成了很复杂的循环嵌套,请教了当时网上的一个大神,当时不明白,后来还是问后端的朋友才理解。)
const mongoose =require('../db'); const { model ,Schema} = require('mongoose'); const PermissionSchema = new Schema({ text:String, type:String, children:Array, userRoles:Array }) const RouteModel = model("parent_routers",PermissionSchema); module.exports = RouteModel
const mongoose =require('../db'); const { model ,Schema} = require('mongoose'); const PermissionSchema = new Schema({ name:String, type:String, text:String, userRole:Array, parentRouterId:String }) const childRouteModel = model("children_routers",PermissionSchema); module.exports = childRouteModel
-
controllers模块下的
const PermissionModel = require('./../models/routers'); const PermissionChildModel = require('./../models/children_routers'); class PermissionCtl { constructor(){ } async query (obj) { // 用户权限查询接口 let { userRole } = obj ; if (!userRole) { return {code:403,msg:'用户权限丢失,请退出后重新登陆!'} } const routersDoc = await PermissionModel.find({userRoles:{$elemMatch:{$eq:userRole}}}); const routersChildDoc = await PermissionChildModel.find({userRole:{$elemMatch:{$eq:userRole}}}); routersDoc.forEach(element => { //console.log(typeof(element._id)); routersChildDoc.forEach(item => { //console.log(typeof(item.parentRouterId)); if (item.parentRouterId==element._id.toString()) { element.children.push(item); } }); }); console.log(routersDoc); return !routersDoc ? {code:403 ,msg :'改用户没有任何权限'}:{code:200,routerList:routersDoc,} } } module.exports = new PermissionCtl()
-
-
效果图
-
代码不全请访问github 获取全部源码
-