前言
第一次在掘金发文章,不对的地方还请大家多多指点。后台管理系统,其中比较重要的一点就是权限控制。最近一年一直在使用vue全家桶进行后台管理系统的开发,说说我的一点心得。
基础页面结构
后台管理系统主要分为一个登录页和主页面,主页面里面的路由是需要进行鉴权的,而权限数据一般都是登录接口返回的。下面是一个常见的路由
export default new Router({
routes: [
{ path: "/", name: "home", component: Home ,redirect: '/index',
children: [ /*这里配置页面路由*/ ]
},
{ path: "/login",name: "login",component: Login },
{ path: "*",redirect: '/'},//其他错误的url重定向到主页
]
});
登录接口返回的权限数据不能直接存储到vuex中,因为在主页面(Home.vue)刷新之后数据就没了,当然了有持久化存储的方案(先不讨论),主页需要的菜单数据,如果是和路由数据差别很大,可以写死,但是不影响我们使用beforeRouteEnter路由钩子鉴权。
改写路由数据
如果想实现路由数据复用到菜单,我们可以在router.js中将路由数据配好,但是不初始化:
const routes= [
{
path: '/',name: 'home',component: () => import('~/Home'),//按需加载
redirect: '/menu1',children: [/*页面的路由,具体请看文末完整demo*/]
},
{ path: '/login', name: 'login', component: () => import('~/Login') },
{ path: "*", redirect: '/login' },
];
export default routes;
然后在main.js中初始化路由数据
import Router from "vue-router";
import routes from "./router";//引入路由数据
Vue.use(Router);
const router = new Router({routes});//实例化路由对象
beforeRouteEnter路由钩子鉴权
home.vue页面的beforeRouteEnter路由钩子触发时机就是第一次进入home.vue页面的时候,但是在这个页面中点击其他菜单不会触发,我们也只需要在第一次进入的时候对菜单进行初始化。
我们需要在Home.vue中利用beforeRouteEnter路由钩子生成权限菜单,并存储权限数据到vuex中,首先在Login.vue页面将登录返回的权限数据存储到本地localStorage或者sessionStorage,然后
在Home.vue中引入路由数据,下面代码中routes就是引入的路由数据
beforeRouteEnter (to, from, next) {
const permission = localStorage.getItem('permission');//也可也使用sessionStorage,看实际情况需要
if(!permission){//没有权限就直接退出
next({ name: "login", replace: true });
}
const rightArr = JSON.parse(permission),
//路由数据过滤无权限的页面并转为菜单数据
transMenu = (arr, parebtPath = '/') =>
arr.filter(item => rightArr.includes(item.meta.code)).map(item => ({
path: parebtPath + item.path,//路由对应的全路径
name: item.meta.name,//路由对应的中文名称
icon: item.meta.icon,//路由对应的icon图标
code: item.meta.code,//路由对应的权限code
children: item.children ? transMenu(item.children, `${parebtPath}${item.path}/`) : null,
})),
menu = transMenu(routes[0].children);
if(!menu.length){//没有菜单权限
next({ name: "login", replace: true });
}
next(vm => {
if(!rightArr.includes(to.meta.code)){//当前url没权限就跳转到第一个菜单
vm.$router.replace(menu[0].path);
}
vm.menuData = menu;
vm.$store.commit("setPermission", rightArr);
})
}
补充说明
-
beforeRouteEnter钩子鉴权是完全可行的,登录之后没权限的菜单入口不会显示,即使你在url里面输入没权限的路由,刷新页面依然会触发beforeRouteEnter。 -
也可以用
beforeEach这个路由钩子进行鉴权,但是个人不倾向于这个办法,每次跳转都检查一下有点浪费,使用主页面的beforeRouteEnter钩子既完成了鉴权,也实现了权限和token的持久化存储。 -
我非常推荐大家使用
localStorage和sessionStorage,cookie仍然存在的价值就是主域名相同的时候可以共享。 -
路由的meta对象还可以添加其他的数据,比如说这个路由不生成菜单,就可以在这里面加一个flag, 然后就是不需要鉴权的菜单可以直接将code设置固定值或者为空,看自己定义。
-
代码里面有一点缺陷就是菜单生成的时候,如果有子孙菜单项的权限,但是没有父级菜单权限,就不会生成菜单数据。解决办法也很简单,将
arr.filter(item => rightArr.includes(item.meta.code))这一段代码改为递归判断子孙节点的权限即可
最后
完整demo的地址:github.com/gongshun/vu…,有什么问题或者代码有啥疑问,欢迎大家评论