资讯专栏INFORMATION COLUMN

详解基于vue,vue-router, vuex以及addRoutes进行权限控制

eternalshallow / 1071人阅读

摘要:基于的权限控制教程,完整代码地址见接下来让我们模拟一个普通用户打开网站的过程,一步一步的走完整个流程。由于权限这块逻辑还挺多,所以在添加了一个模块来处理权限。

基于vuex, vue-router,vuex的权限控制教程,完整代码地址见 https://github.com/linrunzhen...

接下来让我们模拟一个普通用户打开网站的过程,一步一步的走完整个流程。

首先从打开本地的服务localhost:8080开始,我们知道打开后会进入login页面,那么判断的依据是什么。
首先是token。
没有登陆的用户是获取不到token的,而登陆后的角色我们会将token存到local或者seesionStorage 因此,根据当前有没有token即可知道是否登陆。

为了存取token并且方便我们操作,可以配和vuex实现
/* state.js */
export default {
    get UserToken() {
        return localStorage.getItem("token")
    },
    set UserToken(value) {
        localStorage.setItem("token", value)
    }
}
/* mutation.js */
export default {
    LOGIN_IN(state, token) {
        state.UserToken = token
    },
    LOGIN_OUT(state) {
        state.UserToken = ""
    }
}
拦截的判断

没有token进入需要权限的页面:redirect到login页面

由于我们路由是动态挂载的,包括 " " 和404,所以当匹配不到路由时,也重定向到login

router.beforeEach((to, from, next) => {
    if (!store.state.UserToken) {
        if (
            to.matched.length > 0 &&
            !to.matched.some(record => record.meta.requiresAuth)
        ) {
            next()
        } else {
            next({ path: "/login" })
        }
    } 
})

好了,此时用户打开localhost:8080,默认匹配的是""路径,此时我们并没有挂载路由,也没有token,所以来到了login。

输入用户名密码后,有token了,通过store触发 commit("LOGIN_IN") 来设置token。

但是还是没有路由,目前最开始只有login路由

/* 初始路由 */
export default new Router({
    routes: [
        {
            path: "/login",
            component: Login
        }
    ]
})

/* 准备动态添加的路由 */
export const DynamicRoutes = [
    {
        path: "",
        component: Layout,
        name: "container",
        redirect: "home",
        meta: {
            requiresAuth: true,
            name: "首页"
        },
        children: [
            {
                path: "home",
                component: Home,
                name: "home",
                meta: {
                    name: "首页"
                }
            }
        ]
    },
    {
        path: "/403",
        component: Forbidden
    },
    {
        path: "*",
        component: NotFound
    }
]
我们要根据当前用户的token去后台获取权限。

由于权限这块逻辑还挺多,所以在vuex添加了一个permission模块来处理权限。

为了判断是已有路由列表,需要在vuex的permission模块存一个state状态permissionList用来判断,假如permissionList不为null,即已经有路由,如果不存在,就需要我们干活了。

router.beforeEach((to, from, next) => {
    if (!store.state.UserToken) {
        ...
    } else {
        /* 现在有token了 */
        if (!store.state.permission.permissionList) {
            /* 如果没有permissionList,真正的工作开始了 */
            store.dispatch("permission/FETCH_PERMISSION").then(() => {
                next({ path: to.path })
            })
        } else {
            if (to.path !== "/login") {
                next()
            } else {
                next(from.fullPath)
            }
        }
    }
})

来看一下 store.dispatch("permission/FETCH_PERMISSION") 都干了什么

actions: {
    async FETCH_PERMISSION({ commit, state }) {
       /*  获取后台给的权限数组 */
        let permissionList = await fetchPermission()

        /*  根据后台权限跟我们定义好的权限对比,筛选出对应的路由并加入到path=""的children */
        let routes = recursionRouter(permissionList, dynamicRouter)
        let MainContainer = DynamicRoutes.find(v => v.path === "")
        let children = MainContainer.children
        children.push(...routes)

        /* 生成左侧导航菜单 */
        commit("SET_MENU", children)

        setDefaultRoute([MainContainer])

        /*  初始路由 */
        let initialRoutes = router.options.routes

        /*  动态添加路由 */
        router.addRoutes(DynamicRoutes)

        /* 完整的路由表 */
        commit("SET_PERMISSION", [...initialRoutes, ...DynamicRoutes])
    }
}
首先,await fetchPermission()获取后台给的权限数组,格式大概如下
{
    "code": 0,
    "message": "获取权限成功",
    "data": [
        {
            "name": "订单管理",
            "children": [
                {
                    "name": "订单列表"
                },
                {
                    "name": "生产管理",
                    "children": [
                        {
                            "name": "生产列表"
                        }                     
                    ]
                },
                {
                    "name": "退货管理"
                }
            ]
        }
    ]
}
其次根据我们写好的路由数组,进行对比,过滤得到我们要的路由
/* 这里是我们写好的需要权限判断的路由 */
const dynamicRoutes = [
    {
        path: "/order",
        component: Order,
        name: "order-manage",
        meta: {
            name: "订单管理"
        },
        children: [
            {
                path: "list",
                name: "order-list",
                component: OrderList,
                meta: {
                    name: "订单列表"
                }
            },
            {
                path: "product",
                name: "product-manage",
                component: ProductManage,
                meta: {
                    name: "生产管理"
                },
                children: [
                    {
                        path: "list",
                        name: "product-list",
                        component: ProductionList,
                        meta: {
                            name: "生产列表"
                        }
                    },
                    {
                        path: "review",
                        name: "review-manage",
                        component: ReviewManage,
                        meta: {
                            name: "审核管理"
                        }
                    }
                ]
            },
            {
                path: "returnGoods",
                name: "return-goods",
                component: ReturnGoods,
                meta: {
                    name: "退货管理"
                }
            }
        ]
    }
]

export default dynamicRoutes

为了对比,我写好了一个递归函数,用name和meta.name进行对比 ,根据这个函数就可以得到我们想要的结果

/**
 *
 * @param  {Array} userRouter 后台返回的用户权限json
 * @param  {Array} allRouter  前端配置好的所有动态路由的集合
 * @return {Array} realRoutes 过滤后的路由
 */

export function recursionRouter(userRouter = [], allRouter = []) {
    var realRoutes = []
    allRouter.forEach((v, i) => {
        userRouter.forEach((item, index) => {
            if (item.name === v.meta.name) {
                if (item.children && item.children.length > 0) {
                    v.children = recursionRouter(item.children, v.children)
                }
                realRoutes.push(v)
            }
        })
    })
    return realRoutes
}

得到过滤后的数组后,加入到path为""的children下面

{
        path: "",
        component: Layout,
        name: "container",
        redirect: "home",
        meta: {
            requiresAuth: true,
            name: "首页"
        },
        children: [
            {
                path: "home",
                component: Home,
                name: "home",
                meta: {
                    name: "首页"
                }
            },
            
            ...
        ]
    }
这个时候,path为""的children就是我们左侧的导航菜单了,存到state的sidebarMenu待用。加入到children后,这时DynamicRoutes就可以加入到路由了。
/*  动态添加路由 */
router.addRoutes(DynamicRoutes)


 /*  初始路由 */
let initialRoutes = router.options.routes
/* 合并起来,就是完整的路由了 */
commit("SET_PERMISSION", [...initialRoutes, ...DynamicRoutes])
路由添加完了,也就是action操作完毕了,即可在action.then里面调用 next({ path: to.path })进去路由,这里要注意, next里面要传参数即要进入的页面的路由信息,因为next传参数后,当前要进入的路由会被废止,转而进入参数对应的路由,虽然是同一个路由,这么做主要是为了确保addRoutes生效了。
进入路由后,要开始生成左侧菜单,之前我们已经存到sidebarMenu了,现在需要做的只是递归生成菜单而已,虽然用了element的导航菜单,但是为了递归路由,还需要自己封装一下。这里核心的地方是组件的name,在组件里面有children的地方,又再次使用自己,从而遍历整个tree结构的路由。


刷新页面后,根据我们router.beforeEach的判断,有token但是没permissionList,我们是会重新触发action去获取路由的,所以无需担心。但是导航菜单active效果会不见。不过我们已经把el-menu-item的key设置为路由的name,那么我们只要在刷新后,在afterEach把当前路由的name赋值给el-menu default-active即可。同理,在afterEach阶段获取所有matched的路由,即可实现面包屑导航。
if (!store.state.permission.permissionList) {
    store.dispatch("permission/FETCH_PERMISSION").then(() => {
        next({ path: to.path })
    })
} 



...
router.afterEach((to, from, next) => {
    var routerList = to.matched
    store.commit("setCrumbList", routerList)
    store.commit("permission/SET_CURRENT_MENU", to.name)
})
退出登陆后,需要刷新页面,因为我们是通过addRoutes添加的,router没有deleteRoutes这个api,所以清除token,清除permissionList等信息,刷新页面是最保险的。
最后还有一点,每次请求得带上token, 可以对axios封装一下来处理
var instance = axios.create({
    timeout: 30000,
    baseURL
})

// 添加请求拦截器
instance.interceptors.request.use(
    function(config) {
        // 请求头添加token
        if (store.state.UserToken) {
            config.headers.Authorization = store.state.UserToken
        }
        return config
    },
    function(error) {
        return Promise.reject(error)
    }
)

/* axios请求二次封装 */
instance.get = function(url, data, options) {
    return new Promise((resolve, reject) => {
        axios
            .get(url, data, options)
            .then(
                res => {
                    var response = res.data
                    if (response.code === 0) {
                        resolve(response.data)
                    } else {
                        Message.warning(response.message)
                        /* reject(response.message) */
                    }
                },
                error => {
                    if (error.response.status === 401) {
                        Message.warning({
                            message: "登陆超时,请重新登录"
                        })
                        store.commit("LOGIN_OUT")
                        window.location.reload()
                    } else {
                        Message.error({
                            message: "系统异常"
                        })
                    }
                    reject(error)
                }
            )
            .catch(e => {
                console.log(e)
            })
    })
}

export default instance

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/107744.html

相关文章

  • 手摸手,带你用vue撸后台 系列二(登录权限篇)

    摘要:我们将登录按钮上绑上事件,点击登录之后向服务端提交账号和密码进行验证。所以前端和后端权限的划分是不太一致。侧边栏最后一个涉及到权限的地方就是侧边栏,不过在前 完整项目地址:vue-element-admin 系列文章: 手摸手,带你用vue撸后台 系列一(基础篇) 手摸手,带你用vue撸后台 系列二(登录权限篇) 手摸手,带你用vue撸后台 系列三 (实战篇) 手摸手,带你用vu...

    不知名网友 评论0 收藏0
  • vue中如何实现后台管理系统的权限控制

    摘要:二接口访问的权限控制接口权限就是对用户的校验。代码如下按扭权限指令至此为止,权限控制流程就已经完全结束了,在最后我们再看一下完整的权限控制流程图吧五路由控制完整流程图六参考文献手撸后台管理网站之权限控制手摸手,带你用撸后台之权限控制 原文首发于我的博客,欢迎点击查看获得更好的阅读体验~ 一、前言 在广告机项目中,角色的权限管理是卡了挺久的一个难点。首先我们确定的权限控制分为两大部分,其...

    songze 评论0 收藏0
  • vue中如何实现后台管理系统的权限控制

    摘要:二接口访问的权限控制接口权限就是对用户的校验。代码如下按扭权限指令至此为止,权限控制流程就已经完全结束了,在最后我们再看一下完整的权限控制流程图吧五路由控制完整流程图六参考文献手撸后台管理网站之权限控制手摸手,带你用撸后台之权限控制 原文首发于我的博客,欢迎点击查看获得更好的阅读体验~ 一、前言 在广告机项目中,角色的权限管理是卡了挺久的一个难点。首先我们确定的权限控制分为两大部分,其...

    iliyaku 评论0 收藏0
  • vue中如何实现后台管理系统的权限控制

    摘要:二接口访问的权限控制接口权限就是对用户的校验。代码如下按扭权限指令至此为止,权限控制流程就已经完全结束了,在最后我们再看一下完整的权限控制流程图吧五路由控制完整流程图六参考文献手撸后台管理网站之权限控制手摸手,带你用撸后台之权限控制 原文首发于我的博客,欢迎点击查看获得更好的阅读体验~ 一、前言 在广告机项目中,角色的权限管理是卡了挺久的一个难点。首先我们确定的权限控制分为两大部分,其...

    VincentFF 评论0 收藏0
  • addRoutes爬坑记

    摘要:简介用动态路由实现权限控制,是一个很的方案不是么初始路由只有登录页,根据用户的查询对应的权限,然后,将获取到的菜单数据放入和浏览器缓存中。 addRoutes简介 用动态路由实现权限控制,是一个很nice的方案不是么? 初始路由只有登录页,根据用户的id查询对应的权限,然后addRoutes,将获取到的菜单数据放入vuex和浏览器缓存中。 动态添加更多的路由规则。参数必须是一个符合 r...

    draveness 评论0 收藏0

发表评论

0条评论

eternalshallow

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<