美文网首页前端之美-VueJs
六、撸vue/cli 3.+ 的正确姿势(可控的权限管理)

六、撸vue/cli 3.+ 的正确姿势(可控的权限管理)

作者: Baby_ed6e | 来源:发表于2019-04-26 18:28 被阅读189次

1、流行的权限管理方式

1.在路由的meta里定义一个字段,该字段为数组,例如

{
    path: "/",
    name: "home",
    meta: {
        title: "页面"
        roles:['admin','edit']  // 哪个角色可用
    },
    component: () =>
        import("@/views/home/index.vue")
}

2.把需要权限管理的路由单独放在一个数组里,并不在实例化router对象的时候放进去
3.登录时返回用户的token和当前角色,也就是roles,是‘admin’,还是‘edit’,根据这个关键词筛选数组里的路由,然后使用addRoutes添加进已经实例化的router对象中。从而达到对路由的控制。

优点:能够有效实现菜单管理,权限切换,请求接口简洁

弊端:
1.我们可以把路由看做菜单,也就是说,每个角色拥有的菜单是固定死了,是前端写死了,这并没有真正意义上做到权限管理。
2.使用了addRoutes,动态添加了路由,效率低,有更好的方式不采用addRoutes
3.需要些筛选函数,比如我是‘edit’角色,那我需要递归路由树结构,肯定要筛选所有层级的路由,整合所有的路由地址,再添加进去,非常繁琐。
4.把路由按照需要权限和不需要权限两个模块划分,并非整体,阅读起来不够直观(个人认为)

2、路由基本结构例子

理想中我需要如下功能:
1.创建角色,分配角色所拥有的菜单
2.细粒度够细,具体到某个按钮
3.可任意切换角色

概念 :我们都知道每个路由都有别名name,且唯一(除了分组功能的路由没有name,因为模块分组下有默认页面,默认页面已经有了name)

假如入我们的路由结构如下:(大致看结构)
1.有新闻模块大分组,有公告列表和关于我们两个小分组,没有别名name
2.分组下有各种增删改查操作
3.每个模块下的页面都有别名name

import Vue from "vue";
import Router from "vue-router";
import layout from "@/views/layout/index.vue";
import blank from "@/views/layout/blank.vue";

Vue.use(Router);

export default new Router({
    mode: "history",
    base: process.env.BASE_URL,
    routes: [
        {
            path: "/",
            meta: {
                title: "首页"
            },
            component: layout,
            children: [
                {
                    path: "/",
                    name: "home",
                    meta: {
                        title: "欢迎使用"
                    },
                    component: () => import("@/views/home/index.vue")
                },
                {
                    path: "/articles",
                    meta: {
                        title: "文章"
                    },
                    component: blank,
                    children: [
                        {
                            path: "/",
                            meta: {
                                title: "公告"
                            },
                            component: blank,
                            children: [
                                {
                                    path: "/",
                                    name: "articles",
                                    meta: {
                                        title: "公告列表"
                                    },
                                    component: () =>
                                        import("@/views/articles/list.vue")
                                },
                                {
                                    path: "detail/:id",
                                    name: "articles-detail",
                                    meta: {
                                        title: "公告详细"
                                    },
                                    component: () =>
                                        import("@/views/articles/detail.vue")
                                },
                                ....
                            ]
                        },
                        {
                            path: "/",
                            meta: {
                                title: "关于"
                            },
                            component: blank,
                            children: [
                                {
                                    path: "about",
                                    name: "about",
                                    meta: {
                                        title: "关于我们"
                                    },
                                    component: () =>
                                        import(
                                            "@/views/articles/about/about.vue"
                                        )
                                },
                                {
                                    path: "aboutEdit",
                                    name: "about-edit",
                                    meta: {
                                        title: "编辑关于我们"
                                    },
                                    component: () =>
                                        import(
                                            "@/views/articles/about/aboutEdit.vue"
                                        )
                                }
                            ]
                        }
                    ]
                }
            ]
        },
        {
            path: "/login",
            name: "login",
            meta: {
                title: "登录"
            },
            component: () => import("@/views/login/index.vue")
        },
        {
            path: "/403",
            name: "page403",
            meta: {
                title: "403"
            },
            component: () => import("@/views/403.vue")
        },
        {
            path: "/*",
            name: "page404",
            meta: {
                title: "404"
            },
            component: () => import("@/views/404.vue")
        }
    ]
});

3、系统根据路由结构录入路由别名(也叫菜单别名)树结构

分组菜单
页面菜单

这样我们在添加角色的时候,可以轻松的增加或者修改角色


配置角色权限

4、定义常用路由别名

例如无论有无权限都可以访问的登录页,404,401页面等,并且增加路由拦截

const whiteList = ["login", "page404", "page403"];

5、获取当前角色,以及角色对应的 路由别名

登录时候,或者getUserInfo时,获取当前角色,以及角色对应的路由别名的一维列表,合并别名列表

const whiteList = ["login", "page404", "page403"];
// 登录的时候,把路由别名列表放在了store的状态里了。
let roleRouter = [...store.getters.roleRouter, ...whiteList];

6、路由拦截设置

设置路由拦截,如果将要访问的路由的别名没有在 roleRouter 当中,那就直接跳转401页面

import router from "./index";
import store from "@/store/index";
import NProgress from "nprogress";
import "nprogress/nprogress.css";

// 常用白名单路由
const whiteList = ["login", "page404", "page403"];

router.beforeEach((to, from, next) => {
    NProgress.start();
    let roleRouter = [...store.getters.roleRouter, ...whiteList];
    if (!to.name || roleRouter.includes(to.name)) {
        next();
    } else {
        if (to.name === "home") {
            next({
                path: "/login",
                replace: true
            });
        } else {
            next({
                path: "/403",
                replace: true,
                query: {
                    noGoBack: true
                }
            });
        }
    }
});
router.afterEach(() => {
    NProgress.done();
});

7、关于循环菜单

1.根据我们的本地router.js 文件,我们可以循环出左侧菜单的结构
2.增加一个判断就可以实现菜单的管理

let roleRouter = [...store.getters.roleRouter, ...whiteList];

<a-menu-item v-for="(item,index) in router" v-if="roleRouter.includes(item.name)">
      <router-link to="/articles">公告列表</router-link>
</a-menu-item>

8、关于权限细粒度问题

1.同样的道理,我们可以在登录或者getUserInfo的时候,获取角色所拥有的所有api一维列表
2.增加一个判断就可以实现权限细粒度管理

let roleApiList = store.getters.roleApiList
// console.log(roleApiList)
// 类似 ['news/list','news/detail','news/add']  一样的数组

<button v-if="roleApiList.includes('news/add')">
      添加新闻
</button>

相关文章

网友评论

    本文标题:六、撸vue/cli 3.+ 的正确姿势(可控的权限管理)

    本文链接:https://www.haomeiwen.com/subject/hsyqnqtx.html