【vue项目】企业级权限管理

内容

权限管理的应用
vue 项目中的权限管理

前言

权限管理大家一定再熟悉不过,就是不同角色拥有不同的权限(就像我们登陆游戏只能进行自身的基本配置,而管理员就可以进行全服的统一配置)。那么对于 vue 项目如何更好的进行权限的分配,愿本文观点对您有所帮助。

1. 思路

  • 什么是权限管理
    • 根据不同用户渲染不同的菜单
    • 严格控制用户的权限
  • 权限管理的作用
    • 可以让不同的用户有不同的操作
    • 同时也能避免信息的泄漏
  • 实现步骤
    • 动态添加路由(router.addRoutes api 地址:router.addRoutes
    • 对后台数据格式依赖
    • 触发时机(登录验证成功时触发)
    • 导航守卫(控制页面跳转)

实现

当用户登陆成功后,后台返回此用户所拥有的用户信息以及用户权限 ,通过 sessionStoragelocalStoragecookie 等把数据存放在本地。然后通过本地存储把数据存放在 vuex 中进行统一管理(防止刷新页面,vuex 数据丢失),此处用 vuex 对数据进行集中管理,只为后续使用方便。

具体项
  1. 关于后台返回数据
 menus: [ //模拟超级管理员权限数据
        {
    
    
          path: '/',    		//路由地址
          lable: '首页',		//路由名称
          icon: 'discount',		//icon 图标
          url: 'Home/Home',		//路由对应组件路径(生成动态路由时拼接)
        },
        {
    
    
          path: '/video',
          lable: '视频管理',
          icon: 'picture-outline',
          url: 'Video/Video',
        },
        {
    
    
          path: '/user',
          lable: '用户管理',
          icon: 'user',
          url: 'UserMessage/UserMessage',
        },
        {
    
    
          lable: '其他',
          icon: 'coin',
          children: [
            {
    
    
              path: '/page1',
              lable: '页面1',
              icon: 'user',
              url: 'Other/Page1',
            },
            {
    
    
              path: '/page2',
              lable: '页面2',
              icon: 'user',
              url: 'Other/Page2',
            },
          ],
        },
      ],
      token1:'Ivj6eZRx40+MTx2ZvnG8nA', //模拟超级管理员登录返回的 token
      noneMenus:[  //模拟普通用户权限数据
        {
    
    
          path: '/',
          lable: '首页',
          icon: 'discount',
          url: 'Home/Home',
        },
        {
    
    
          path: '/video',
          lable: '视频管理',
          icon: 'picture-outline',
          url: 'Video/Video',
        }
      ],
      token2:'Ivj6eZRx40+MTx2ZvnG8nB' //模拟普通用户登录返回的 token

注意:此数据既用于菜单栏的显示,也用于动态的渲染路由;其中的 token 字符串用于模拟登录身份验证,其将保存在本地(下文包含)

  1. vuex 中的数据
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
    
    
    state: {
    
    
        isCollapse: false,
        menu: [],
        token: '',
        currentMenu: null,
        todoList: [{
    
    
            path: '/',
            lable: '首页',
            icon: 'discount',
        }]
    },
    mutations: {
    
    
        // 登陆成功把菜单列表存在sessionStrong中
        setMenu(state, val) {
    
    
            state.menu = val;
            sessionStorage.setItem('menu', JSON.stringify(val))
        },
        // 清空sessionStrong
        cleareMenu(state) {
    
    
            state.menu = [];
            sessionStorage.removeItem('menu');
        },
        // 根据后台返回数据(动态渲染路由)
        addRouter(state, router) {
    
    
            if (!sessionStorage.getItem('menu')) {
    
     //首先判断 sessionStorage 中是否存在 menu
                return
            }
            let menu = JSON.parse(sessionStorage.getItem('menu')); //从本地存储中获取菜单
            state.menu = menu; //避免舒心state 数据丢失
            // 在菜单下追加动态路由
            let currentMenu = [{
    
      //基本路由(每个用户皆有)
                path: '/',
                component: () =>
                    import (`../views/Main/Main.vue`),
                children: []
            }]
            menu.forEach(item => {
    
    
                if (item.children) {
    
    
                    item.children = item.children.map(item => {
    
    
                        item.component = () =>
                            import ('../views/' + item.url)
                        return item
                    })
                    currentMenu[0].children.push(...item.children)
                } else {
    
    
                    item.component = () =>
                        import ('../views/' + item.url)
                    currentMenu[0].children.push(item)
                }
            })
            router.addRoutes(currentMenu)
        },
        // 保存后台返回的token
        setToken(state, val) {
    
    
            state.token = val;
            sessionStorage.setItem('token', val);
        },
        // 清空token
        clearToken(state) {
    
    
            state.token = '';
            sessionStorage.removeItem('token')
        },
        selectMenu(state, val) {
    
    
            // val.path != '/' ? state.currentMenu = val : state.currentMenu = null
            if (val.path != '/') {
    
    
                state.currentMenu = val;
                let result = state.todoList.some(item => item.path == val.path) //#判断是否已经添加过
                result == true ? '' : state.todoList.push(val)
            } else {
    
    
                state.currentMenu = null
            }
        },
        changeCollapse(state) {
    
    
            state.isCollapse = !state.isCollapse
        },
        closeTab(state, val) {
    
     //#关闭菜单,删除对应的标签
            let index = state.todoList.findIndex(item => item.path == val.path);
            state.todoList.splice(index, 1)
        }
    },
    actions: {
    
    },
    modules: {
    
    }
})

注意:此处动态追加路由方法为服务器端返回了路由对应的路径。若服务器无返回路径,我们可以定义全部需要显示的路由,然后根据菜单 名称 或 路径 进行匹配追加

  1. 当用户登录时(点击登录按钮)
methods: {
    
    
    submitForm(formName) {
    
    
      this.$refs[formName].validate((valid) => {
    
     //表单验证(数据格式效验)
        if (valid) {
    
    
          if (this.ruleForm.name == 'cwen' && this.ruleForm.pass == '123456') {
    
     //超级管理员账号
            this.$store.commit('cleareMenu'); //先清除菜单,防止用户二次登录(后期用路由守卫登录拦截解决)
            this.$store.commit('setMenu', this.menus); // 通过 vuex 把获取的后台数据存在sessionStorage 中
            this.$store.commit('setToken',this.token1); // 通过vuex 把后台返回的token 保存在sessionStorage 中
            this.$store.commit('addRouter', this.$router); // 根据后台数据,动态生成路由
            this.$router.push('/'); // 生成动态路由成功后跳转,主页面 (渲染根组件时刷新)
          } else if (
            //普通用户账号
            this.ruleForm.name == 'none' &&
            this.ruleForm.pass == '123456'
          ) {
    
    
            this.$store.commit('cleareMenu'); //先清除菜单,防止用户二次登录(后期用路由守卫登录拦截解决)
            this.$store.commit('setMenu', this.noneMenus); // 通过 vuex 把获取的后台数据存在sessionStorage 中
            this.$store.commit('setToken',this.token2); // 通过vuex 把后台返回的token 保存在sessionStorage 中
            this.$store.commit('addRouter', this.$router); // 根据后台数据,动态生成路由
            this.$router.push('/'); // 生成动态路由成功后跳转,主页面 (渲染根组件时刷新)
          } else {
    
    
            return this.$message.error('用户名或密码有误');
          }
        } else {
    
    
          return false;
        }
      });
    },

//---------------------------华丽分割线------------------------------------------------------
//main.js
//实例渲染时用钩子获取路由数据(防止路由追加失败)
new Vue({
    
    
    router,
    store,
    render: h => h(App),
    created() {
    
    
        store.commit('addRouter', router)
    }
}).$mount('#app')

补充:此处的用户登录为简洁模拟,但基本业务逻辑皆可实现。关于菜单栏的数据渲染,可参照 NavMenu 导航菜单 进行自行配置

  1. 动态追加路由后的 router.js
import Vue from 'vue'
import VueRouter from 'vue-router'

Vue.use(VueRouter)

const routes = [{
    
    
        path: '/login',
        component: () =>
            import ( /* webpackChunkNmae:"login"*/ '../views/Login/Login.vue'),
    }
    // 此处路由全部由后台返回数据,动态渲染
    // {
    
    
    // path: '/',
    // component: () =>
    //     import ( /*webpackChunkName: "home" */ '../views//Main/Main.vue'),
    // children: [{
    
    
    //     path: '/',
    //     component: () =>
    //         import ( /*webpackChunkName: "home" */ '../views/Home/Home.vue')
    // }, {
    
    
    //     path: '/video',
    //     component: () =>
    //         import ( /*webpackChunkName: "home" */ '../views/Video/Video.vue')
    // }, {
    
    
    //     path: '/user',
    //     component: () =>
    //         import ( /*webpackChunkName: "home" */ '../views/UserMessage/UserMessage.vue')
    // }, {
    
    
    //     path: '/page1',
    //     component: () =>
    //         import ( /*webpackChunkName: "home" */ '../views/Other/Page1.vue')
    // }, {
    
    
    //     path: '/page2',
    //     component: () =>
    //         import ( /*webpackChunkName: "home" */ '../views/Other/Page2.vue')
    // }]
    // }
]

const router = new VueRouter({
    
    
    routes
})

router.beforeEach((to, from, next) => {
    
     //路由守卫 完成身份验证
    // 防止处于首页的用户访问登录页
    if (sessionStorage.getItem('token') && to.path == '/login') return next('/');
    // 判断用户是否处于登录状态
    if (!sessionStorage.getItem('token') && to.path !== '/login') {
    
    
        next('/login')
    } else {
    
    
        next()
    }
})

//# 重复点击报错问题(降低版本 或者 加以下代码(对 router.push() 方法进行更改);也可使用菜单的 router 属性,进行点击路由跳转)
const originalPush = VueRouter.prototype.push
VueRouter.prototype.push = function push(location) {
    
    
    return originalPush.call(this, location).catch(err => err)
}

export default router

注意:注释部分为之前的静态路由,router.beforeEach() 路由守卫阻止用户无权限跳转

come on ヾ(●´∀`●)

猜你喜欢

转载自blog.csdn.net/cwq521o/article/details/108432843