• Vue.js快速入门之七:系统权限管理


            每个系统项目中,都离不开权限的管理。有些小项目为了快速开发,可能会省略权限的处理;但中大型项目无法回避,中大型系统使用的人员比较繁杂,每个人身处不同职位或职权,所看到信息是不一样的。

            权限管理,一般是先建“角色”,给角色添加某些栏目显示权限,或赋予增删改权限,再将“角色”指派给对应用户。一个用户可能只有一个角色,或多个角色,这样该用户则拥有了这些“角色”的权限集合。

    • 如A用户的角色为“超级管理员”,则拥有所有权限,可任意查看系统任何页面和功能;
    • 如B用户的角色为运维人员,则只能看到运维部分功能页面,以及查看和编辑的权限。

            下面我们通过代码,来处理栏目显示的权限,来了解前端是否何处理权限问题的。

    一、新建db.js,存储模拟数据

            前面几篇已介绍了如何使用mockjs以及定义模拟数据,这里不再阐述,代码如下:

    1. import { Random } from 'mockjs'
    2. const DBdata = new Map();
    3. DBdata.set('栏目列表', [
    4. {
    5. "name": "首页",
    6. "path": "/home/index",
    7. "permission": ["view-home"],
    8. "icon": "el-icon-data-line"
    9. },
    10. {
    11. "name": "栏目管理",
    12. "path": "/category/index",
    13. "permission": ["view-category"],
    14. "icon": "el-icon-data-board",
    15. },
    16. {
    17. "name": "用户管理",
    18. "path": "/user/index",
    19. "permission": ["view-user"],
    20. "icon": "el-icon-pie-chart"
    21. },
    22. {
    23. "name": "角色管理",
    24. "path": "/role/index",
    25. "permission": ["view-role"],
    26. "icon": "el-icon-data-analysis"
    27. },
    28. {
    29. "name": "设置管理",
    30. "path": "/setting/index",
    31. "permission": ["view-setting"],
    32. "icon": "el-icon-collection-tag",
    33. }
    34. ]);
    35. DBdata.set('用户信息', {
    36. username: 'test',
    37. avatar: '',
    38. company: '',
    39. rolename: '管理员',
    40. roles: ['view-home', 'view-category', 'view-user', 'view-setting']
    41. });
    42. export default DBdata;

            如上代码,如果用户roles列表中,将view-setting去除,则该用户则无显示系统设置的权限,该栏目也不会显示。

    二、创建模拟接口数据

            新建mock/index.js,用来定义模拟接口,代码如下:

    1. import { mock } from 'mockjs'
    2. import DBData from '@/db'
    3. /**
    4. * 获取栏目列表信息
    5. */
    6. mock('/api/user/info', 'get', (request, response) => {
    7. return {
    8. code: 200,
    9. data: {
    10. userinfo: DBData.get('用户信息').map(item => item),
    11. menu: DBData.get('栏目列表').map(item => item),
    12. token: 'kjadfakldho134koadf82389adfklkj123409jaldkfaljk2134j'
    13. },
    14. message: 'success'
    15. };
    16. });

    三、创建API请求

            api中创建index.js,创建接口请求功能函数,对axios请求进行封装的request.js文件,前端篇幅已讲解,代码直接拿过来使用即可。

    1. import Service from '@/utils/request'
    2. export const getUserInfo = () => {
    3. return Service.get('/api/user/info', {});
    4. }

    四、定义store状态管理器

            store状态管理器中,添加相关数据。以下主要为两项功能:

    1. 通过getters计算出拥有显示权限栏目列表
    2. 添加actions业务功能函数,在路由勾子函数中,添加权限过滤,如不在权限内的则无法通过路由直接访问。

    4.1 state定义变量

            在store中state.js中定义相应变量,代码如下:

    1. const state = {
    2. //栏目列表
    3. category: [],
    4. //访问令牌
    5. token: '',
    6. //用户信息
    7. userInfo: {},
    8. //角色列表
    9. roles: []
    10. }
    11. export default state;

    4.2 getters定义相应计算属性

            getters.js文件中定义,这里重点是categoryRoleList函数,返回可显示的栏目数据,栏目列表中获取该列表数据进行显示。代码如下:

    1. const getters = {
    2. userInfo(state){
    3. let { username, company } = state.userInfo;
    4. return { username, company };
    5. },
    6. roleList(state){
    7. return state.roles;
    8. },
    9. categoryList(state){
    10. return state.category;
    11. },
    12. //获取可显示的栏目列表
    13. categoryRoleList(state){
    14. return state.category.filter(
    15. item => Array.isArray(item['permission'])&&
    16. (
    17. item.permission.length==0 || //没有权限信息的,直接通过
    18. state.roles.filter(sub => item.permission.includes(sub.roleName)).length>0 //在权限数组中的,可以通过
    19. )
    20. );
    21. }
    22. }
    23. export default getters;

    4.3 mutationsType类型定义

            mutationsType.js文件中定义以下常量值,代码如下:

    1. export const USERINFO = "USERINFO";
    2. export const TOKEN = "TOKEN";
    3. export const ROLES = "ROLES";
    4. export const CATEGORY_LIST = "CATEGORY_LIST";

    4.4 mutations.js文件

            mutations定义函数修改变量值,代码如下:

    1. import { USERINFO, TOKEN, ROLES, CATEGORY_LIST } from './../mutationsType'
    2. const mutations = {
    3. [USERINFO](state, param){
    4. state.userInfo = param;
    5. },
    6. [TOKEN](state, param){
    7. state.token = param;
    8. }
    9. [CATEGORY_LIST](state, param){
    10. state.category = param;
    11. },
    12. [ROLES](state, param){
    13. state.roles = param;
    14. }
    15. }
    16. export default mutations;

    4.5 actions定义业务逻辑函数

            这里只仅使用了vuex状态管理器进行存储数据,需要做数据可持久化,可以增加localstorage或sessionstorage进行本地存储,页面刷新或重新访问页面时,把本地数据重新缓存到vuex中。

            这里重点是checkRolesRoutePermission业务函数,放到router的勾子函数中,用来阻止无访问权限的路由跳转。代码如下:

    1. import { USERINFO, TOKEN, ROLES, CATEGORY_LIST } from './../mutationsType'
    2. const actions = {
    3. //登录信息
    4. loginInfo({commit}, params){
    5. commit(USERINFO, params.userinfo);
    6. commit(TOKEN, params.token);
    7. commit(ROLES, params.roles);
    8. commit(CATEGORY_LIST, params.menu);
    9. },
    10. //检测路由是否有权限
    11. checkRolesRoutePermission({ commit, getters }, param){
    12. let DGFilter = (_child, _path) => {
    13. return Array.isArray(_child) && _child.length>0 && _child.filter( item => item.path == _path || DGFilter(item['children'], _path) ).length > 0;
    14. };
    15. return new Promise((resolve, reject) => {
    16. let { categoryList, roleList } = getters,
    17. //筛选出当前路径对应的一线栏目
    18. filterMenuList = categoryList.filter(
    19. item => item.path == param || DGFilter(item['children'], param)
    20. );
    21. //如果菜单列表中有筛选到数据,进入处理,否则放行
    22. if(filterMenuList.length>0){
    23. let _permission = filterMenuList[0]['permission'];
    24. //如果菜单列表中权限列表长度大于0,表示有权限数据,进入处理, 否则放行
    25. if(Array.isArray(_permission)&&_permission.length>0){
    26. //判断角色列表中,是否存在该该权限,存在则可放行,不存在,无法通行
    27. if( roleList.filter(item => _permission.includes(item.roleName)).length>0 ){
    28. resolve();
    29. }else{
    30. reject({
    31. code: 404,
    32. message: "当前页面无访问权限"
    33. });
    34. }
    35. }else{
    36. resolve();
    37. }
    38. });
    39. },
    40. }
    41. export default actions;

    五、路由中添加校验权限

            在router目录中创建index.js文件,定义好系统路由,并通过勾子函数对路由进行过滤处理。通过路由进行页面跳转时,如没有访问权限,则直接跳转到无访问权限页面。getters中已获取有权限访问的栏目列表,则从栏目中访问无权限页面已被屏蔽,如在浏览器地址栏直接通过路由访问,则会通过以下代码中,路由卫士进行拦截。

    1. import Vue from 'vue'
    2. import Router from 'vue-router'
    3. import store from '@/store'
    4. //......
    5. let _router = new Router({
    6. routes: [
    7. {
    8. path: '/',
    9. name: 'Index',
    10. component: Index,
    11. },
    12. //......
    13. {
    14. path: '/no-permission',
    15. name: 'Error405',
    16. component: Error405,
    17. },
    18. {
    19. path: '*',
    20. name: 'Error404',
    21. component: Error404,
    22. },
    23. ]
    24. });
    25. _router.beforeEach((toRoute, fromRoute, next) => {
    26. //检测路由是否权限
    27. store.dispatch('checkRolesRoutePermission', toRoute.path).then(res => {
    28. if(toRoute.path!=fromRoute.path){
    29. next();
    30. }
    31. }).catch(e => {
    32. if(toRoute.path!=fromRoute.path){
    33. next(e.code==405?'/no-permission':'/error');
    34. }
    35. });
    36. });
    37. export default _router;

    六、用户信息和菜单权限获取

    6.1 实现登录功能

            在用户登录成功时,就可以获取到用户的角色信息,以及栏目列表信息。将用户信息,访问令牌,权限数据等信息存储到Store状态管理器中代码如下:

    1. <script>
    2. import { getUserInfo } from '@/api/index'
    3. export default {
    4. data(){
    5. return {
    6. }
    7. },
    8. methods: {
    9. //登录事件
    10. login(){
    11. //do something....
    12. this.updateUserinfo();
    13. },
    14. //获取用户信息
    15. updateUserinfo(){
    16. getUserInfo({
    17. //传入登录信息
    18. //...
    19. }).then(res => {
    20. if(res.code==200){
    21. //将用户信息存储到vuex中
    22. this.$store.dispatch('loginInfo', {
    23. userinfo: res.data['userinfo'],
    24. token: res.data['token'],
    25. menu: res.data['menu'],
    26. roles: res.data['userinfo']['roles']
    27. });
    28. }
    29. });
    30. }
    31. }
    32. }
    33. </script>

            以上登录页面只展示了业务部分代码,dom部分大家可以使用element-ui组合快速搭建。

    6.2 栏目列表显示

            显示已进行权限过滤的栏目列表,代码如下:

    1. <template>
    2. <div class="category-list">
    3. <div class="list-item"
    4. v-for="(item, index) in categoryRoleList"
    5. :key="index">
    6. <h3>{{item.name}}</h3>
    7. </div>
    8. </div>
    9. </template>
    10. <script>
    11. import { mapGetters } from 'vuex'
    12. export default {
    13. data(){
    14. return {
    15. }
    16. },
    17. computed: {
    18. //获取可显示栏目列表
    19. ...mapGetters(['categoryRoleList'])
    20. },
    21. methods: {
    22. }
    23. }
    24. </script>

    以上讲解的是通过后台动态获取所有栏目及其所对应的权限,以及动态获取用户的角色信息,进行相互匹配进行的权限显示和访问限制。以上仅供大家参考与学习。

  • 相关阅读:
    Python实用记录(十四):python统计某个单词在TXT/JSON文件中出现的次数
    TCP协议之《内存空间管理》
    关于4diac的发布/订阅和客户端/服务端功能块的思考
    pyinstaller 操作以及常见问题解决
    攻击者利用漏洞发动DDoS攻击,3万台GitLab服务器仍未修补
    鸿蒙系统(HarmonyOS)--第一章
    如何搭建安全的 CI/CD 管道?
    React之DOM的diff算法
    Nginx配置参数详解一篇到底
    vue3 的 ref、 toRef 、 toRefs
  • 原文地址:https://blog.csdn.net/jiciqiang/article/details/115548377