博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
详解基于vue,vue-router, vuex以及addRoutes进行权限控制
阅读量:6595 次
发布时间:2019-06-24

本文共 9540 字,大约阅读时间需要 31 分钟。

基于vuex, vue-router,vuex的权限控制教程,完整代码地址见

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

首先从打开本地的服务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 = ''    }}

拦截的判断

  1. 没有token进入需要权限的页面:redirect到login页面
  2. 由于我们路由是动态挂载的,包括 ' ' 和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

转载地址:http://fqcio.baihongyu.com/

你可能感兴趣的文章
java基础-Eclipse开发工具介绍
查看>>
JS常见的字符串操作
查看>>
洛谷P1069 细胞分裂 数学
查看>>
JAVA中的编码分析
查看>>
查看源代码Source not found及在eclipse中配置jdk的src.zip源代码
查看>>
uniGUI试用笔记(二)
查看>>
HOG特征-理解篇
查看>>
Microsoft.AlphaImageLoader滤镜解说
查看>>
extjs_02_grid(显示本地数据,显示跨域数据)
查看>>
超过响应缓冲区限制
查看>>
ubuntu 下安装 matplotlib
查看>>
webservice的几个简单概念
查看>>
underscore 1.7.0 api
查看>>
spring Transaction Management --官方
查看>>
iOS开发-清理缓存功能的实现
查看>>
IS_ERR、PTR_ERR、ERR_PTR
查看>>
html5 canvas 奇怪的形状垂直渐变
查看>>
mac java环境
查看>>
lamp 一键安装
查看>>
SQL Server 2008 收缩日志(log)文件
查看>>