美文网首页前端之美-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