您的当前位置:首页Vue 权限控制的两种方法(路由验证)
广告

Vue 权限控制的两种方法(路由验证)

2023-12-06 来源:哗驼汽车网

下面介绍两种权限控制的方法:

  • 路由元信息(meta)
  • 动态加载菜单和路由(addRoutes)
  • 路由元信息(meta)

    如果一个网站有不同的角色,比如 管理员 和 普通用户 ,要求不同的角色能访问的页面是不一样的

    这个时候我们就可以 把所有的页面都放在路由表里 ,只要 在访问的时候判断一下角色权限 。如果有权限就让访问,没有权限的话就拒绝访问,跳转到404页面

    vue-router 在构建路由时提供了元信息 meta 配置接口,我们可以在元信息中添加路由对应的权限,然后在路由守卫中检查相关权限,控制其路由跳转。

    可以在每一个路由的 meta 属性里,将能访问该路由的角色添加到 roles 里。用户每次登陆后,将用户的角色返回。然后在访问页面时,把路由的 meta 属性和用户的角色进行对比,如果用户的角色在路由的 roles 里,那就是能访问,如果不在就拒绝访问。

    代码示例1:

    路由信息:

    routes: [ { path: '/login', name: 'login', meta: { roles: ['admin', 'user'] }, component: () => import('../components/Login.vue') }, { path: 'home', name: 'home', meta: { roles: ['admin'] }, component: () => import('../views/Home.vue') },]

    页面控制:

    //假设有两种角色:admin 和 user //从后台获取的用户角色const role = 'user'//当进入一个页面是会触发导航守卫 router.beforeEach 事件router.beforeEach((to,from,next)=>{ if(to.meta.roles.includes(role)){ next() //放行 }esle{ next({path:"/404"}) //跳到404页面 }})

    代码示例2

    当然也可以用下面的一种方法:

    // router.js// 路由表元信息[ { path: '', redirect: '/home' }, { path: '/home', meta: { title: 'Home', icon: 'home' } }, { path: '/userCenter', meta: { title: '个人中心', requireAuth: true // 在需要登录的路由的meta中添加响应的权限标识 } }]// 在守卫中访问元信息function gaurd (to, from, next) { // to.matched.some(record => record.meta.requireAuth) // 可在此处}

    可以在多个路由下面添加这个权限标识,达到控制的目的

    只要一切换页面,就需要看有没有这个权限,所以可以在最大的路由下 main.js 中配置

    存储信息

    一般的,用户登录后会在本地存储用户的认证信息,可以用 token 、 cookie 等,这里我们用 token 。

    将用户的 token 保存到 localStorage 里,而用户信息则存在内存 store 中。这样可以在 vuex 中存储一个标记用户登录状态的属性 auth ,方便权限控制。

    代码示例

    // store.js{ state: { token: window.localStorage.getItem('token'), auth: false, userInfo: {} }, mutations: { setToken (state, token) { state.token = token window.localStorage.setItem('token', token) }, clearToken (state) { state.token = '' window.localStorage.setItem('token', '') }, setUserInfo (state, userInfo) { state.userInfo = userInfo state.auth = true // 获取到用户信息的同时将auth标记为true,当然也可以直接判断userInfo } }, actions: { async getUserInfo (ctx, token) { return fetchUserInfo(token).then(response => { if (response.code === 200) { ctx.commit('setUserInfo', response.data) } return response }) }, async login (ctx, account) { return login(account).then(response => { if (response.code === 200) { ctx.commit('setUserInfo', response.data.userInfo) ctx.commit('setToken', response.data.token) } }) } }}

    写好路由表和vuex之后,给所有路由设置一个全局守卫,在进入路由之前进行权限检查,并导航到对应的路由。

    // store.js{ state: { token: window.localStorage.getItem('token'), auth: false, userInfo: {} }, mutations: { setToken (state, token) { state.token = token window.localStorage.setItem('token', token) }, clearToken (state) { state.token = '' window.localStorage.setItem('token', '') }, setUserInfo (state, userInfo) { state.userInfo = userInfo state.auth = true // 获取到用户信息的同时将auth标记为true,当然也可以直接判断userInfo } }, actions: { async getUserInfo (ctx, token) { return fetchUserInfo(token).then(response => { if (response.code === 200) { ctx.commit('setUserInfo', response.data) } return response }) }, async login (ctx, account) { return login(account).then(response => { if (response.code === 200) { ctx.commit('setUserInfo', response.data.userInfo) ctx.commit('setToken', response.data.token) } }) } }}

    上述的方法是基于 jwt 认证方式,本地不持久化用户信息,只保存 token ,当用户刷新或者重新打开网页时,进入需要登录的页面都会尝试去请求用户信息,该操作在整个访问过程中只进行一次,直到刷新或者重新打开,对于应用后期的开发维护和扩展支持都很好。

    动态加载菜单和路由(addRoutes)

    有时候为了安全,我们需要根据用户权限或者是用户属性去动态的添加菜单和路由表,可以实现对用户的功能进行定制。 vue-router 提供了 addRoutes() 方法,可以动态注册路由, 需要注意的是,动态添加路由是在路由表中 push 路由,由于路由是按顺序匹配的,因此需要将诸如404页面这样的路由放在动态添加的最后。

    代码示例

    // store.js// 将需要动态注册的路由提取到vuex中const dynamicRoutes = [ { path: '/manage', name: 'Manage', meta: { requireAuth: true }, component: () => import('./views/Manage') }, { path: '/userCenter', name: 'UserCenter', meta: { requireAuth: true }, component: () => import('./views/UserCenter') }]

    在 vuex 中添加 userRoutes 数组用于存储用户的定制菜单。在setUserInfo中根据后端返回的菜单生成用户的路由表。

    // store.jssetUserInfo (state, userInfo) { state.userInfo = userInfo state.auth = true // 获取到用户信息的同时将auth标记为true,当然也可以直接判断userInfo // 生成用户路由表 state.userRoutes = dynamicRoutes.filter(route => { return userInfo.menus.some(menu => menu.name === route.name) }) router.addRoutes(state.userRoutes) // 注册路由}

    修改菜单渲染

    // App.vue<div id="nav"> <router-link to="/">主页</router-link> <router-link to="/login">登录</router-link> <template v-for="(menu, index) of $store.state.userInfo.menus"> <router-link :to="{ name: menu.name }" :key="index">{{menu.title}}</router-link> </template></div>

    总结

    以上所述是小编给大家介绍的Vue 权限控制的两种方法(路由验证),希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对脚本之家网站的支持!如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

    小编还为您整理了以下内容,可能对您也有帮助:

    vue中如何实现后台管理系统的权限控制的方法步骤

    一、前言

    在广告机项目中,角色的权限管理是卡了挺久的一个难点。首先我们确定的权限控制分为两大部分,其中根据粒的大小分的更细:

    接口访问的权限控制

    页面的权限控制

    菜单中的页面是否能被访问

    页面中的按钮(增、删、改)的权限控制是否显示下面我们就看一看是如何实现这些个权限控制的。

    二、接口访问的权限控制

    接口权限就是对用户的校验。正常来说,在用户登录时服务器需要给前台返回一个Token,然后在以后前台每次调用接口时都需要带上这个Token,

    然后服务端获取到这个Token后进行比对,如果通过则可以访问。

    现有的做法是在登录成功的回调中将后台返回的Token直接存储到 sessionStorag​e ,然在请求时将Token取出放入headers中传给后台,代码如下:

    this.$http({

    method: 'get',

    url: 'test/query?id=20',

    withCredentials: true,

    headers: {

    token: sessionStorage.getItem('token'),

    name: sessionStorage.getItem('name') //应后台需求传的用户名

    }

    }).then(response => {

    //请求成功后的操作

    })

    后来在一些文章中发现axios可以在中直接将Token塞入 config.headers.Authorization 中,作为全局传入。下面是代码部分:

    //main.js

    import axios from 'axios'

    // 实例化Axios,并进行超时设置

    const service = axios.create({

    timeout: 5000

    })

    // baseURL

    // axios.defaults.baseURL = 'https://api.github.com';

    // http request

    // 每次请求都为http头增加Authorization字段,其内容为token

    service.interceptors.request.use(

    config => {

    if (store.state.user.token) {

    config.headers.Authorization = `token ${store.state.user.token}`;

    }

    return config

    },

    err => {

    return Promise.reject(err)

    }

    );

    export default service

    三、页面权限控制

    在前面已经说到,页面权限控制又分为两种:

    菜单中的页面是否能被访问

    页面中的按钮(增、删、改)的权限控制是否显示

    这些权限一般是在固定页面进行配置,保存后记录到数据库中。

    按钮权限暂且不提,页面访问权限在实现中又可以分为两种方式:

    显示所有菜单,当用户访问不在自己权限内的菜单时,提示权限不足

    只显示当前用户能访问的权限内菜单,如果用户通过URL进行强制访问,则会直接进入404

    既然展现出来后不能点,那算几个意思,逗我玩儿呢?所谓眼不见为净,综合考虑后,肯定是方案二比较符合良好的用户体验。

    好,我们现在梳理一下大致的页面访问权限的流程:

    在对流程梳理完成后我们开始进行详细的编写。

    1、创建路由表

    创建路由表实际上没有什么难度,照着vue-router官方文档给的示例直接写就行了。但是因为有部分页面是不需要访问权限的,

    所以需要将登录、404、维护等页面写到默认的路由中,而将其它的需要权限的页面写到一个变量或者一个文件中,这样可

    以有效的减轻后续的维护压力。

    下面将index.js的代码贴上,异步路由将适量减少,以免占过多篇幅。

    // router/index.js

    import Vue from 'vue'

    import Router from 'vue-router'

    import App from '@/App'

    import store from '../store/index'

    Vue.use(Router);

    //手动跳转的页面白名单

    const whiteList = [

    '/'

    ];

    //默认不需要权限的页面

    const constantRouterMap = [

    {

    path: '/',

    name: '登录',

    component: (resolve) => require(['@/components/login'], resolve)

    },

    {

    path: '/index',

    name: 'nav.Home',

    component: (resolve) => require(['@/components/index'], resolve)

    },

    {

    path: '/templateMake',

    name: '模板制作',

    component: (resolve) => require(['@/components/Template/templateMake'], resolve)

    },

    {

    path: '/programMack',

    name: '节目制作',

    component: (resolve) => require(['@/components/Template/programMack'], resolve)

    },

    {

    path: '/release',

    name: '节目发布',

    component: (resolve) => require(['@/components/Program/release'], resolve)

    }

    ]

    //注册路由

    export const router = new Router({

    routes: constantRouterMap

    });

    //异步路由(需要权限的页面)

    export const asyncRouterMap = [

    {

    path: '/resource',

    name: 'nav.Resource',

    meta: {

    permission: []

    },

    component: (resolve) => require(['@/components/Resource/resource'], resolve)

    },

    {

    path: '/template',

    name: 'nav.Template',

    meta: {

    permission: []

    },

    component: (resolve) => require(['@/components/Template/template'], resolve)

    },

    {

    path: '/generalSet',

    name: 'nav.System',

    meta: {

    permission: []

    },

    component: (resolve) => require(['@/components/SystemSet/generalSet'], resolve)

    },

    {

    path: '',

    name: 'nav.Log',

    component: App,

    children: [

    {

    path: '/userLog',

    name: 'nav.UserLog',

    meta: {

    permission: []

    },

    component: (resolve) => require(['@/components/Log/userLog'], resolve),

    },

    {

    path: '/operatingLog',

    name: 'nav.SystemLog',

    meta: {

    permission: []

    },

    component: (resolve) => require(['@/components/Log/operatingLog'], resolve),

    },

    ]

    }

    ]

    ];

    注意事项:这里有一个需要非常注意的地方就是 404 页面一定要最后加载,如果放在constantRouterMap一同声明了404,后面的所以页面都会被拦截到404,详细的问题见 addRoutes when you've got a wildcard route for 404s does not work

    2、页面访问权限

    在开始时我们梳理了一个大致的页面访问权限流程。下面我们先实现最核心的部分:

    我们首先获取用户权限列表,在这里我们将接触到vuex状态管理,官方文档有详细介绍,这里就不过多描述了,下面请看代码:

    // store/index.js

    import Axios from 'axios'

    import Vue from 'vue'

    import Vuex from 'vuex'

    Vue.use(Vuex);

    const axios = Axios.create();

    const state = {

    mode: 'login',

    list: []

    };

    const getters = {};

    const mutations = {

    setMode: (state, data) => {

    state.mode = data

    },

    setList: (state, data) => {

    state.list = data

    }

    };

    const actions = {

    // 获取权限列表

    getPermission({commit}) {

    return new Promise((resolve, reject) => {

    axios({

    url: '/privilege/queryPrivilege?id=' + sessionStorage.getItem('privId'),

    methods: 'get',

    headers: {

    token: sessionStorage.getItem('token'),

    name: sessionStorage.getItem('name')

    }

    }).then((res) => {

    // 存储权限列表

    commit('setList', res.data.cust.privileges[0].children);

    resolve(res.data.cust.privileges[0].children)

    }).catch(() => {

    reject()

    })

    })

    }

    };

    export default new Vuex.Store({

    state,

    mutations,

    actions,

    getters

    })

    好了,我们现在请求后台拿到了权限数据,并将数据存放到了vuex中,下面我们需要利用返回数据匹配之前写的异步路由表,将匹配结果和静态路由表结合,开成最终的实际路由表。

    其中最关键的是利用vue-router2.2.0版本新添加的一个addRoutes方法,我们看看官方文档如何解释此方法的:

    router.addRoutes(routes) 2.2.0+ 动态添加更多的路由规则。参数必须是一个符合 routes 选项要求的数组。

    那我们现在就可以开始使用addRoutes进行路由匹配了。下面看代码:

    // router/index.js

    /**

    * 根据权限匹配路由

    * @param {array} permission 权限列表(菜单列表)

    * @param {array} asyncRouter 异步路由对象

    */

    function routerMatch(permission, asyncRouter) {

    return new Promise((resolve) => {

    const routers = [];

    // 创建路由

    function createRouter(permission) {

    // 根据路径匹配到的router对象添加到routers中即可

    permission.forEach((item) => {

    if (item.children && item.children.length) {

    createRouter(item.children)

    }

    let path = item.path;

    // 循环异步路由,将符合权限列表的路由加入到routers中

    asyncRouter.find((s) => {

    if (s.path === '') {

    s.children.find((y) => {

    if (y.path === path) {

    y.meta.permission = item.permission;

    routers.push(s);

    }

    })

    }

    if (s.path === path) {

    s.meta.permission = item.permission;

    routers.push(s);

    }

    })

    })

    }

    createRouter(permission)

    resolve([routers])

    })

    }

    然后我们编写导航钩子

    // router/index.js

    router.beforeEach((to, form, next) => {

    if (sessionStorage.getItem('token')) {

    if (to.path === '/') {

    router.replace('/index')

    } else {

    console.log(store.state.list.length);

    if (store.state.list.length === 0) {

    //如果没有权限列表,将重新向后台请求一次

    store.dispatch('getPermission').then(res => {

    //调用权限匹配的方法

    routerMatch(res, asyncRouterMap).then(res => {

    //将匹配出来的权限列表进行addRoutes

    router.addRoutes(res[0]);

    next(to.path)

    })

    }).catch(() => {

    router.replace('/')

    })

    } else {

    if (to.matched.length) {

    next()

    } else {

    router.replace('/')

    }

    }

    }

    } else {

    if (whiteList.indexOf(to.path) >= 0) {

    next()

    } else {

    router.replace('/')

    }

    }

    });

    到这里我们已经完成了对页面访问的权限控制,接下来我们来讲解一下操作按扭的权限部分。

    四、数据操作权限

    是否还记得前面的路由配置中我们多出来的一个代码,下面我们拿出来看看:

    //异步路由(需要权限的页面)

    export const asyncRouterMap = [

    {

    path: '/resource',

    name: 'nav.Resource',

    meta: {

    permission: []

    },

    component: (resolve) => require(['@/components/Resource/resource'], resolve)

    },

    {

    path: '/template',

    name: 'nav.Template',

    meta: {

    permission: []

    },

    component: (resolve) => require(['@/components/Template/template'], resolve)

    },

    {

    path: '/generalSet',

    name: 'nav.System',

    meta: {

    permission: []

    },

    component: (resolve) => require(['@/components/SystemSet/generalSet'], resolve)

    },

    {

    path: '',

    name: 'nav.Log',

    component: App,

    children: [

    {

    path: '/userLog',

    name: 'nav.UserLog',

    meta: {

    permission: []

    },

    component: (resolve) => require(['@/components/Log/userLog'], resolve),

    },

    {

    path: '/operatingLog',

    name: 'nav.SystemLog',

    meta: {

    permission: []

    },

    component: (resolve) => require(['@/components/Log/operatingLog'], resolve),

    },

    ]

    }

    ]

    ];

    为每个路由页面增加meta字段。在routerMatch函数中将匹配到的详细权限字段赋值到这里。这样在每个页面的route对象中就会得到这个字段。

    asyncRouter.find((s) => {

    if (s.path === '') {

    s.children.find((y) => {

    if (y.path === path) {

    //赋值

    y.meta.permission = item.permission;

    routers.push(s);

    }

    })

    }

    if (s.path === path) {

    s.meta.permission = item.permission;

    routers.push(s);

    }

    })

    接下来我们编写一个vue自定义指令对页面中需要进行鉴权的元素进行判断,比如类似这样的:

    <a @click="upload" v-allow="'3'"></a> /* 3代表一个上传权限的ID,权限中有3则显示按钮 */

    我们直接注册一个全局指令,利用vnode来访问vue的方法。代码如下:

    //main.js

    //按扭权限指令

    Vue.directive('allow', {

    inserted: (el, binding, vnode) => {

    let permissionList = vnode.context.$route.meta.permission;

    if (!permissionList.includes(binding.value)) {

    el.parentNode.removeChild(el)

    }

    }

    })

    至此为止,权限控制流程就已经完全结束了,在最后我们再看一下完整的权限控制流程图吧.

    五、路由控制完整流程图

    六、参考文献

    Vue + ElementUI 手撸后台管理网站之权限控制

    手摸手,带你用vue撸后台之权限控制

    Vue实战篇|使用路由管理用户权限(动态路由)

    权限控制是后台管理系统比较常见的需求,如果我们需要对某些页面的添加权限控制的话,那我们可以在路由管理中的权限做一些校验,没有通过权限校验的给出相应的提示或者直接跳转到报错页面。

    跟着我一起来学vue实战篇路由管理权限吧!

    getCurrentAuthority()函数用于获取当前用户权限,一般来源于后台数据

    check()函数用于权限的校验匹配

    isLogin()函数用于检验用户是否登录

    路由配置元信息meta:{ authority: ["admin"] }

    使用to.matched获取跳转路由的全部信息,包括父路由和子路由

    使用lodash中的findLast匹配离跳转路由配置权限的元信息

    引入auth.js中check()和isLogin()进行判断是否具有权限或是否已登录

    如果没有权限则给出提示信息后跳转到403页面,未登录则返回登录页面

    运行结果

    当getCurrentAuthority()函数返回admin时,则菜单会显示所有元信息meta:{ authority: ["admin"] }的路由菜单;

    如返回值为user时,菜单会显示所有元信息meta:{ authority: ["user"] }的路由菜单

    接下来CrabFort会带大家一起实现更加精细化的权限设计(权限组件、权限指令)

    Vue实战篇|使用路由管理用户权限(动态路由)

    权限控制是后台管理系统比较常见的需求,如果我们需要对某些页面的添加权限控制的话,那我们可以在路由管理中的权限做一些校验,没有通过权限校验的给出相应的提示或者直接跳转到报错页面。

    跟着我一起来学vue实战篇路由管理权限吧!

    getCurrentAuthority()函数用于获取当前用户权限,一般来源于后台数据

    check()函数用于权限的校验匹配

    isLogin()函数用于检验用户是否登录

    路由配置元信息meta:{ authority: ["admin"] }

    使用to.matched获取跳转路由的全部信息,包括父路由和子路由

    使用lodash中的findLast匹配离跳转路由配置权限的元信息

    引入auth.js中check()和isLogin()进行判断是否具有权限或是否已登录

    如果没有权限则给出提示信息后跳转到403页面,未登录则返回登录页面

    运行结果

    当getCurrentAuthority()函数返回admin时,则菜单会显示所有元信息meta:{ authority: ["admin"] }的路由菜单;

    如返回值为user时,菜单会显示所有元信息meta:{ authority: ["user"] }的路由菜单

    接下来CrabFort会带大家一起实现更加精细化的权限设计(权限组件、权限指令)

    如何优雅地在vue中添加权限控制示例详解

    前言

    在一个项目中,一些功能会涉及到重要的数据管理,为了确保数据的安全,我们会在项目中加入权限来每个用户的操作。作为前端,我们要做的是配合后端给到的权限数据,做页面上的各种各样的。

    需求

    因为这是一个工作上的业务需求,所以对于我来说主要有两个地方需要进行权限控制。

    第一个是侧边菜单栏,需要控制显示与隐藏。

    第二个就是页面内的各个按钮,弹窗等。

    流程

    1、如何获取用户权限?

    后端(当前用户拥有的权限列表)-> 前端(通过后端的接口获取到,下文中我们把当前用户的权限列表叫做 permissionList)

    2、前端如何做?

    通过产品的需求,在项目中进行权限点的配置,然后通过 permissionList 寻找是否有配置的权限点,有就显示,没有就不显示。

    3、然后呢?

    没了。

    当我刚开始接到这个需求的时候就是这么想的,这有什么难的,不就获取 permissionList 然后判断就可以了嘛。后来我才发现真正的需求远比我想象的复杂。

    真正的问题

    上面的需求有提到我们主要解决两个问题,侧边菜单栏的显示 & 页面内操作。

    假设我们有这样一个路由的设置(以下只是一个例子):

    import VueRouter from 'vue-router'

    /* 注意:以下配置仅为部分配置,并且省去了 component 的配置 */

    export const routes = [

    {

    path: '/',

    name: 'Admin',

    label: '首页'

    },

    {

    path: '/user',

    name: 'User',

    label: '用户',

    redirect: { name: 'UserList' },

    children: [

    {

    path: 'list',

    name: 'UserList',

    label: '用户列表'

    },

    {

    path: 'group',

    name: 'UserGroup',

    label: '用户组',

    redirect: { name: 'UserGroupList' },

    children: [

    {

    path: 'list',

    name: 'UserGroupList',

    label: '用户组列表'

    },

    {

    path: 'config',

    name: 'UserGroupConfig',

    label: '用户组设置'

    }

    ]

    }

    ]

    },

    {

    path: '/setting',

    name: 'Setting',

    label: '系统设置'

    },

    {

    path: '/login',

    name: 'Login',

    label: '登录'

    }

    ]

    const router = new VueRouter({

    routes

    })

    export default router

    其中前两级路由会显示在侧边栏中,第就不会显示在侧边栏中了。

    页面内操作的权限设置不需要考虑很多其他东西,我们主要针对侧边栏以及路由进行问题的分析,通过分析,主要有以下几个问题:

    什么时候获取 permissionList,如何存储 permissionList

    子路由全都没权限时不应该显示本身(例:当用户列表和用户组都没有权限时,用户也不应该显示在侧边栏)

    默认重定向的路由没有权限时,应寻找 children 中有权限的一项重定向(例:用户路由重定向到用户列表路由,若用户列表没有权限,则应该重定向到用户组路由)

    当用户直接输入没有权限的 url 时需要跳转到没有权限的页面或其他操作。(路由)

    下面我们针对以上问题一个一个解决。

    什么时候获取权限,存储在哪 & 路由

    我这里是在 router 的 beforeEach 中获取的,获取的 permissionList 是存放在 vuex 中。

    原因是考虑到要做路由的,以及方便后面项目中对权限列表的使用,以下是实现的示例:

    首先我们加入权限配置到 router 上:

    // 以下只展示部分配置

    {

    path: '/user',

    name: 'User',

    label: '用户',

    meta: {

    permissions: ['U_1']

    },

    redirect: { name: 'UserList' },

    children: [

    {

    path: 'list',

    name: 'UserList',

    label: '用户列表',

    meta: {

    permissions: ['U_1_1']

    }

    },

    {

    path: 'group',

    name: 'UserGroup',

    label: '用户组',

    meta: {

    permissions: ['U_1_2']

    },

    redirect: { name: 'UserGroupList' },

    children: [

    {

    path: 'list',

    name: 'UserGroupList',

    label: '用户组列表',

    meta: {

    permissions: ['U_1_2_1']

    }

    },

    {

    path: 'config',

    name: 'UserGroupConfig',

    label: '用户组设置',

    meta: {

    permissions: ['U_1_2_2']

    }

    }

    ]

    }

    ]

    }

    可以看到我们把权限加在了 meta 上,是为了更简单的从 router.beforeEch 中进行权限判断,权限设置为一个数组,是因为一个页面可能涉及多个权限。

    接下来我们设置 router.beforeEach :

    // 引入项目的 vuex

    import store from '@/store'

    // 引入判断是否拥有权限的函数

    import { includePermission } from '@/utils/permission'

    router.beforeEach(async (to, from, next) => {

    // 先判断是否为登录,登录了才能获取到权限,怎么判断登录就不写了

    if (!isLogin) {

    try {

    // 这里获取 permissionList

    await store.dispatch('getPermissionList')

    // 这里判断当前页面是否有权限

    const { permissions } = to.meta

    if (permissions) {

    const hasPermission = includePermission(permissions)

    if (!hasPermission) next({ name: 'NoPermission' })

    }

    next()

    }

    } else {

    next({ name: 'Login' })

    }

    })

    我们可以看到我们需要一个判断权限的方法 & vuex 中的 getPermissionList 如下:

    // @/store

    export default {

    state: {

    permissionList: []

    },

    mutations: {

    updatePermissionList: (state, payload) => {

    state.permissionList = payload

    }

    },

    actions: {

    getPermissionList: async ({ state, commit }) => {

    // 这里是为了防止重复获取

    if (state.permissionList.length) return

    // 发送请求方法省略

    const list = await api.getPermissionList()

    commit('updatePermissionList', list)

    }

    }

    }

    // @/utils/permission

    import store from '@/store'

    /**

    * 判断是否拥有权限

    * @param {Array<string>} permissions - 要判断的权限列表

    */

    function includePermission (permissions = []) {

    // 这里要判断的权限没有设置的话,就等于不需要权限,直接返回 true

    if (!permissions.length) return true

    const permissionList = store.state.permissionList

    return !!permissions.find(permission => permissionList.includes(permission))

    }

    重定向问题

    以上我们解决了路由的基本配置与权限如何获取,怎么路由跳转,接下来我们要处理的就是重定向问题了。

    这一点可能和我们项目本身架构有关,我们项目的侧边栏下还有子级,是以下图中的 tab 切换展现的,正常情况当点击药品管理后页面会重定向到入库管理的 tab 切换页面,但当入库管理没有权限时,则应该直接重定向到出库管理界面。

    所以想实现以上的效果,我需要重写 router 的 redirect,做到可以动态判断(因为在我配置路由时并不知道当前用户的权限列表)

    然后我查看了 vue-router 的文档,发现了 redirect 可以是一个方法,这样就可以解决重定向问题了。

    vue-router 中 redirect 说明 ,根据说明我们可以改写 redirect 如下:

    // 我们需要引入判断权限方法

    import { includePermission } from '@/utils/permission'

    const children = [

    {

    path: 'list',

    name: 'UserList',

    label: '用户列表',

    meta: {

    permissions: ['U_1_1']

    }

    },

    {

    path: 'group',

    name: 'UserGroup',

    label: '用户组',

    meta: {

    permissions: ['U_1_2']

    }

    }

    ]

    const routeDemo = {

    path: '/user',

    name: 'User',

    label: '用户',

    redirect: (to) => {

    if (includePermission(children[0].meta.permissions)) return { name: children[0].name }

    if (includePermission(children[1].meta.permissions)) return { name: children[1].name }

    },

    children

    }

    虽然问题解决了,但是发现这样写下去很麻烦,还要修改 router 的配置,所以我们使用一个方法生成:

    // @/utils/permission

    /**

    * 创建重定向函数

    * @param {Object} redirect - 重定向对象

    * @param {string} redirect.name - 重定向的组件名称

    * @param {Array<any>} children - 子列表

    */

    function createRedirectFn (redirect = {}, children = []) {

    // 避免缓存太大,只保留 children 的 name 和 permissions

    const permissionChildren = children.map(({ name = '', meta: { permissions = [] } = {} }) => ({ name, permissions }))

    return function (to) {

    // 这里一定不能在 return 的函数外面筛选,因为权限是异步获取的

    const hasPermissionChildren = permissionChildren.filter(item => includePermission(item.permissions))

    // 默认填写的重定向的 name

    const defaultName = redirect.name || ''

    // 如果默认重定向没有权限,则从 children 中选择第一个有权限的路由做重定向

    const firstPermissionName = (hasPermissionChildren[0] || { name: '' }).name

    // 判断是否需要修改默认的重定向

    const saveDefaultName = !!hasPermissionChildren.find(item => item.name === defaultName && defaultName)

    if (saveDefaultName) return { name: defaultName }

    else return firstPermissionName ? { name: firstPermissionName } : redirect

    }

    }

    然后我们就可以改写为:

    // 我们需要引入判断权限方法

    import { includePermission, createRedirectFn } from '@/utils/permission'

    const children = [

    {

    path: 'list',

    name: 'UserList',

    label: '用户列表',

    meta: {

    permissions: ['U_1_1']

    }

    },

    {

    path: 'group',

    name: 'UserGroup',

    label: '用户组',

    meta: {

    permissions: ['U_1_2']

    }

    }

    ]

    const routeDemo = {

    path: '/user',

    name: 'User',

    label: '用户',

    redirect: createRedirectFn({ name: 'UserList' }, children),

    children

    }

    这样稍微简洁一些,但我还是需要一个一个路由去修改,所以我又写了一个方法来递归 router 配置,并重写他们的 redirect:

    // @/utils/permission

    /**

    * 创建有权限的路由配置(多级)

    * @param {Object} config - 路由配置对象

    * @param {Object} config.redirect - 必须是 children 中的一个,并且使用 name

    */

    function createPermissionRouter ({ redirect, children = [], ...others }) {

    const needRecursion = !!children.length

    if (needRecursion) {

    return {

    ...others,

    redirect: createRedirectFn(redirect, children),

    children: children.map(item => createPermissionRouter(item))

    }

    } else {

    return {

    ...others,

    redirect

    }

    }

    }

    这样我们只需要在最外层的 router 配置加上这样一层函数就可以了:

    import { createPermissionRouter } from '@/utils/permission'

    const routesConfig = [

    {

    path: '/user',

    name: 'User',

    label: '用户',

    meta: {

    permissions: ['U_1']

    },

    redirect: { name: 'UserList' },

    children: [

    {

    path: 'list',

    name: 'UserList',

    label: '用户列表',

    meta: {

    permissions: ['U_1_1']

    }

    },

    {

    path: 'group',

    name: 'UserGroup',

    label: '用户组',

    meta: {

    permissions: ['U_1_2']

    },

    redirect: { name: 'UserGroupList' },

    children: [

    {

    path: 'list',

    name: 'UserGroupList',

    label: '用户组列表',

    meta: {

    permissions: ['U_1_2_1']

    }

    },

    {

    path: 'config',

    name: 'UserGroupConfig',

    label: '用户组设置',

    meta: {

    permissions: ['U_1_2_2']

    }

    }

    ]

    }

    ]

    }

    ]

    export const routes = routesConfig.map(item => createPermissionRouter(item))

    const router = new VueRouter({

    routes

    })

    export default router

    当然这样写还有一个好处,其实你并不需要设置 redirect,这样会自动重定向到 children 的第一个有权限的路由

    侧边栏显示问题

    我们的项目使用的是根据路由的配置来生成侧边栏的,当然会加一些其他的参数来显示显示层级等问题,这里就不写具体代码了,如何解决侧边栏 children 全都无权限不显示的问题呢。

    这里我的思路是,把路由的配置也一同更新到 vuex 中,然后侧边栏配置从 vuex 中的配置来读取。

    由于这个地方涉及修改的东西有点多,而且涉及业务,我就不把代码拿出来了,你可以自行实验。

    方便团队部署权限点的方法

    以上我们解决了大部分权限的问题,那么还有很多涉及到业务逻辑的权限点的部署,所以为了团队中其他人可以优雅简单的部署权限点到各个页面中,我在项目中提供了以下几种方式来部署权限:

    通过指令 v-permission 来直接在 template 上设置

    <div v-permission="['U_1']"></div>

    通过全局方法 this.$permission 判断,因为有些权限并非在模版中的

    {

    hasPermission () {

    // 通过方法 $permission 判断是否拥有权限

    return this.$permission(['U_1_1', 'U_1_2'])

    }

    }

    这里要注意,为了 $permission 方法的返回值是可被监测的,判断时需要从 this.$sto

    在VueJS中如何设置用户权限

    本篇文章主要给大家讲述了VueJS应用中管理用户权限的详细过程和方法,以及相关的代码展示,需要的朋友参考下吧。

    在需要身份验证的前端应用里,我们经常想通过用户角色来决定哪些内容可见。比如,游客身份可以阅读文章,但注册用户或管理员才能看到编辑按钮。

    在前端中管理权限可能会有点麻烦。你之前可能写过这样的代码:

    if (user.type === ADMIN || user.auth && post.owner === user.id ) {

    ...

    }作为代替方案,一个简洁轻量的库——CASL——可以让管理用户权限变得非常简单。只要你用CASL定义了权限,并设置了当前用户,就可以把上面的代码改为这样:

    if (abilities.can('update', 'Post')) {

    ...

    }在这篇文章里,我会展示如何在前端应用里使用Vue.js和CASL来管理权限。

    CASL 速成课程CASL可以让你定义一系列规则来哪些资源对用户可见。

    比如,CASL规则能够标明用户可以对给定的资源和实例(帖子、文章、评论等)进行哪些CRUD(Create, Read, Update和Delete)操作。

    假设我们有分类广告网站。最显而易见的规则就是:

    游客可以浏览所有帖子

    管理员可以浏览所有帖子,并且可以更新或删除

    使用CASL,我们用AbilityBuilder来定义规则。调用can来定义一条新规则。例如:

    onst { AbilityBuilder } = require('casl');

    export function(type) {

    AbilityBuilder.define(can => {

    switch(type) {

    case 'guest':

    can('read', 'Post');

    break;

    case 'admin':

    can('read', 'Post');

    can(['update', 'delete'], 'Post');

    break;

    // Add more roles here

    }

    }

    };现在,就可以用定义的规则来检查应用权限了。

    import defineAbilitiesFor from './abilities';

    let currentUser = {

    id: 999,

    name: "Julie"

    type: "registered",

    };

    let abilities = defineAbilitiesFor(currentUser.type);

    Vue.component({

    template: `<p><p>

    <p>Please log in</p>

    `,

    props: [ 'post' ],

    computed: {

    showPost() {

    return abilities.can('read', 'Post');

    }

    }

    });Demo 课程作为演示,我做了一个用来展示分类广告帖子的服务器/客户端应用。这个应用的规则是:用户能够阅读帖子或发帖,但是只能更新或删除自己的帖子。

    我用Vue.js和CASL来方便地运行和扩展这些规则,即使以后添加新的操作或实例也将很方便。

    现在我就带你一步步搭建这个应用。如果你想一睹为快,请戳这个Github repo。

    定义用户权限我们在 resources/ability.js中定义用户权限。CASL的一个优点是与环境无关,也就是说它既能在Node中运行,也能在浏览器中运行。

    我们会把权限定义写到一个CommonJS模块里来保证Node的兼容性(Webpack能让这个模块用在客户端)。

    resources/ability.js

    const casl = require('casl');

    mole.exports = function defineAbilitiesFor(user) {

    return casl.AbilityBuilder.define(

    { subjectName: item => item.type },

    can => {

    can(['read', 'create'], 'Post');

    can(['update', 'delete'], 'Post', { user: user });

    }

    );

    };下面我们来剖析这段代码。

    define方法的第二个参数,我们通过调用can来定义了权限规则。这个方法的第一个参数是你要允许的CRUD操作,第二个是资源或实例,在这个例子中是Post。

    注意第二个can的调用,我们传了一个对象作为第三个参数。这个对象是用来测试user属性是否匹配我们提供的user对象。如果我们不这么做,那不光创建者可以删帖,谁都可以随便删了。

    resources/ability.js

    ...

    casl.AbilityBuilder.define(

    ...

    can => {

    can(['read', 'create'], 'Post');

    can(['update', 'delete'], 'Post', { user: user });

    }

    );CASL检查实例来分配权限时,需要知道实例的type。一种解决方式是把具有subjectName方法的对象,作为define方法的第一个参数,subjectName方法会返回实例的类型。

    我们通过在实例中返回type来达成目的。我们需要保证,在定义Post对象时,这个属性是存在的。

    resources/ability.js

    ...

    casl.AbilityBuilder.define(

    { subjectName: item => item.type },

    ...

    );最后,我们把我们的权限定义封装到一个函数里,这样我们就可以在需要测试权限的时候直接传进一个user对象。在下面的函数中会更易理解。

    resources/ability.js

    const casl = require('casl');

    mole.exports = function defineAbilitiesFor(user) {

    ...

    };Vue 中的访问权限规则现在我们想在前端应用中检查一个对象中,用户具有哪些CRUD权限。我们需要在Vue组件中访问CASL规则。这是方法:

    引入Vue和 abilities plugin。这个插件会把CASL加到Vue的原型上,这样我们就能在组件内调用了。

    在Vue 应用内引入我们的规则(例: resources/abilities.js)。

    定义当前用户。实战中,我们是通过服务器来获取用户数据的,在这个例子中,我们简单地硬编码到到项目里。

    牢记,abilities模块export一个函数,我们把它称为defineAbilitiesFor。我们会向这个函数传入用户对象。现在,无论何时,我们可以通过检测一个对象来得出当前用户拥有何种权限。

    添加abilities插件,这样我们就可以在组件中像这样来进行测试了:this.$can(...)。

    src/main.js

    import Vue from 'vue';

    import abilitiesPlugin from './ability-plugin';

    const defineAbilitiesFor = require('../resources/ability');

    let user = { id: 1, name: 'George' };

    let ability = defineAbilitiesFor(user.id);

    Vue.use(abilitiesPlugin, ability);Post 实例我们的应用会使用分类广告的帖子。这些表述帖子的对象会从数据库中检索,然后被服务器传给前端。比如:

    我们的Post实例中有两个属性是必须的:

    type属性。CASL会使用 abilities.js中的subjectName回调来检查正在测试的是哪种实例。

    user属性。这是发帖者。记住,用户只能更新和删除他们发布的帖子。在 main.js中我们通过defineAbilitiesFor(user.id)已经告诉了CASL当前用户是谁。CASL要做的就是检查用户的ID和user属性是否匹配。

    let posts = [

    {

    type: 'Post',

    user: 1,

    content: '1 used cat, good condition'

    },

    {

    type: 'Post',

    user: 2,

    content: 'Second-hand bathroom wallpaper'

    }

    ];这两个post对象中,ID为1的George,拥有第一个帖子的更新删除权限,但没有第二个的。

    在对象中测试用户权限帖子通过Post组件在应用中展示。先看一下代码,下面我会讲解:

    src/components/Post.vue

    <template>

    <p>

    <p>

    <br /><small>posted by </small>

    </p>

    <button @click="del">Delete</button>

    </p>

    </template>

    <script> import axios from 'axios';

    export default {

    props: ['post', 'username'],

    methods: {

    del() {

    if (this.$can('delete', this.post)) {

    ...

    } else {

    this.$emit('err', 'Only the owner of a post can delete it!');

    }

    }

    }

    } </script>

    <style lang="scss">...</style>点击Delete按钮,捕获到点击事件,会调用del处理函数。

    我们通过this.$can('delete', post)来使用CASL检查当前用户是否具有操作权限。如果有权限,就进一步操作,如果没有,就给出错误提示“只有发布者可以删除!”

    服务器端测试在真实项目里,用户在前端删除后,我们会通过 Ajax发送删除指令到接口,比如:

    src/components/Post.vue

    if (this.$can('delete', post)) {

    axios.get(`/delete/${post.id}`, ).then(res => {

    ...

    });

    }服务器不应信任客户端的CRUD操作,那我们把CASL测试逻辑放到服务器:

    server.js

    app.get("/delete/:id", (req, res) => {

    let postId = parseInt(req.params.id);

    let post = posts.find(post => post.id === postId);

    if (ability.can('delete', post)) {

    posts = posts.filter(cur => cur !== post);

    res.json({ success: true });

    } else {

    res.json({ success: false });

    }

    });CASL是同构(isomorphic)的,服务器上的ability对象就可以从abilities.js中引入,这样我们就不必复制任何代码了!

    封装此时,在简单的Vue应用里,我们就有非常好的方式管理用户权限了。

    我认为this.$can('delete', post) 比下面这样优雅得多:

    if (user.id === post.user && post.type === 'Post') {

    ...

    }上面是我整理给大家的,希望今后会对大家有帮助。

    相关文章:

    JS中的单例模式实现对数据增删改查

    使用Vue仿制今日头条(详细教程)

    React开发如何配置eslint

    哗驼汽车网还为您提供以下相关内容希望对您有帮助:

    vue 动态路由/路由权限 解决方案

    1.菜单栏/导航栏(一级权限) 在登录成功后,获取后端的权限数据, 根据权限数据,展示对应的路由导航或菜单即可;2.界面的控制 如果用户没有登录, 用户手动在地址栏输入路由地址,则需要跳转到登录界面. 如果用户已经...

    如何优雅地在vue中添加权限控制示例详解

    默认重定向的路由没有权限时,应寻找 children 中有权限的一项重定向(例:用户路由重定向到用户列表路由,若用户列表没有权限,则应该重定向到用户组路由) 当用户直接输入没有权限的 url 时需要跳转到没有权限的页面或其他操作。(路由限制...

    vue中如何实现后台管理系统的权限控制的方法步骤

    好了,我们现在请求后台拿到了权限数据,并将数据存放到了vuex中,下面我们需要利用返回数据匹配之前写的异步路由表,将匹配结果和静态路由表结合,开成最终的实际路由表。其中最关键的是利用vue-router2.2.0版本新添加的一个addRoutes方法,我们...

    vue不用router怎么判断路径权限

    实现控制的方式分两种:1、通过vue-routeraddRoutes方法注入路由实现控制,2、通过vue-routerbeforeEach钩子限制路由跳转

    vue项目 动态路由怎么做

    vue项目实现 动态路由 的方式大体可分为两种:前端这边把路由写好,登录的时候根据用户的角色权限来动态展示路由,(前端控制路由) 详情可参阅 花裤衩大佬 的项目 手把手...后台传来当前用户对应权限的路由表,前端通过调...

    Vue项目前后端分离下的前端鉴权方案

    前端Vue全家桶,后台.net。 ### 需求分析 1. 前端路由鉴权,屏蔽地址栏入侵 2. 路由数据由后台管理,前端只按固定规则异步加载路由 3. 权限控制精确到每一个按钮 4. 自动更新token 5. 同一个浏览器只能登录一个账号 ### 前端方案...

    Vue的两种路由模式

    在vue-router路由对象中,路由有两种模式:hash和history,而默认的是hash模式.因为hash发生变化的url都会被浏览器记录下来,从而会发现浏览器的前进后退都可以用 修改历史状态 包括了pushState,replaceState两个方法,这两个方法...

    前端权限控制

    在登入时我们把权限数据存入vuex中并本地化,通过路由对象可以获取到路由的配置,把那个用户的路由单独添加到路由列表中,使用addroutes添加更改后到路由配置,添加动态路由的方法调用在app.vue的created中,因为每次加载页面都会...

    vue添加权限管理中怎么引入permission.js

    新建index.js 将permission转为install:在main.js中用use方法使用permission:在模板中使用v-permission控制访问权限:第二种方式:Vue.prototype+install+v-if实现用户角色权限控制:新建permission.js,将checkPer方法注册到Vue....

    Vue实现动态路由

    通常我们在vue项目中都是前端配置好路由的,但在一些项目中我们可能会遇到权限控制,这样我们就涉及到 动态路由 的设置了。动态路由设置一般有两种 :(1)、简单的角色路由设置:比如只涉及到管理员和普通用户的权限。通常直接...

    Top