动态添加后台管理权限 (前端+后端,附代码)

最近我的一个大学城家教项目准备上线了,预计用户量会达到不错的预期。随之,后台管理中审核用户学生信息的工作量会增大。所以我打算,开多几个后台账号分别给不同的管理员使用,这里就需要配置后台的权限管理

我在网上搜寻到的很多文章教程仅有两个角色的权限管理,在我的项目中不太足够,所以我将写一个动态添加后台管理权限的demo。

技术栈

  • 前端:Vue2
  • 后端:Koa + MongoDB

思路

  1. 前端先把所有的页面和菜单功能都写好
  2. 前后端沟通
  • 每个路由对应的字段,前端在router/index.js,中根据字段的映射,把对应路由添加到当前路由下
  • 每个菜单对应的字段,这部分的映射放在后端,可以减少前端打包的体积,把菜单对应的数据发给后端
    3.创建角色管理模块,注册用户,并给用户添加相应权限

具体实现

先看看整体的结构

all.png

一共两个菜单,三个路由,对应的映射如下;

aside.png

roumap.png

后端代码

注册接口

routerMap中的数据是前端菜单的渲染数据,由前端提前测试好后,传给后端

let routerMap = {
  'userManger': {
    id: 1,
    authName: '权限管理',
    icon: 'el-icon-connection',
    children: [
      {
        id: 11,
        authName: '用户页面',
        icon: 'el-icon-s-grid',
        path: 'table',
        rights: [] //存放对应菜单的功能权限
      },
      {
        id: 12,
        authName: '素材页面',
        icon: 'el-icon-s-marketing',
        path: 'image',
        rights: [] //存放对应菜单的功能权限
      }
    ]
  },
  'test': {
    id: 2,
    authName: '测试菜单',
    icon: 'el-icon-set-up',
    children: [
      {
        id: 21,
        authName: '测试页面',
        icon: 'el-icon-s-custom',
        path: 'users',
        rights: [] //存放对应菜单的功能权限
      }
    ]
  }
}


const signup = (form) => {
  let {username,password,type,type2,role} = form;
  let rights = [];
  // type => 菜单权限  ,  type => 功能权限
  type.forEach(item => {
    let route = JSON.parse(JSON.stringify(routerMap[item]));
    let children = route.children.map(item=>{
      item.rights = type2;
      return item;
    })
    route.children = children;
    rights.push(route);
  })
  
  // 在数据库中生成数据
  const users = new Users({
      username,
      password,
      role,
      rights //存放菜单的数据,前端可以直接渲染
  })
  return users.save()
}

需要鉴权的接口管理

解密出token中保留的用户角色role,例如:角色管理模块的接口,只有超级管理员可以操作这部分,都通过中间件判断role === 'admin',不是的话就返回错误信息。不了解JWT的同学,可以看看我往期的文章 轻松解决JWT鉴权并实现简单权限管理

const jwt = require('jsonwebtoken') 
const { secretKey } = require('../config/config')

tokenRouter.delete('/deleteUsers', Auth.middleware,async ctx => {
  const { username } = ctx.request.body;
  // console.log(form)
  let res = await remove(username)
  console.log(username)
  ctx.body = {
    res
  }
})

class Auth { 
    get middleware() {
        return async (ctx, next) => { 
            const token = ctx.request.header.authorization.split(' ')[1]; 
            // 解密出token中的保留的信息 
            let result = jwt.verify(token, secretKey) 
            if (result.role !== 'admin') { 
                ctx.body = { errCode: 1005, msg: '权限不足', }
                return 
            } 
        await next() 
        } 
    } 
} 
module.exports = Auth

到这一步后端的工作已全部完成啦🎉🎉

前端代码

用户注册

注册时就把对应的的权限发给后端,这部分就不上代码了吧哈哈哈

我这里设置了两个角色,便于后面做接口的权限管理。实际开发中只要按自己的需求去定制就可以啦。

signUp.png

用户登录

将菜单数据存到VueX中,并动态添加路由规则

async onLogin () {
      this.loading = true
      let result = await login(this.form);
      if(result.data.errCode == 10001){
        this.$message.error(result.data.msg);
        this.loading = false
        return;
      }
      
      const {role,username,photo,rights} = result.data.user
      this.$store.commit('setRole', role);
      localStorage.setItem('token', result.data.token);
      
      // 把菜单数据存到VueX中
      this.$store.commit('setRightList', rights)
      this.loading = false
      this.$message.success('登陆成功')

      // 根据用户所具备的权限 动态添加路由规则
      initDynamicRoutes()
      this.$router.push('/home')
    }

动态添加路由规则

// 动态路由
const tableRule = {
  path: '/home/table',
  name: 'table',
  component: () => import('@/views/table/index.vue')
}
const imageRule = {
  path: '/home/image',
  name: 'image',
  component: () => import('@/views/image')
}
const userRule = {
  path: '/home/users',
  name: 'users',
  component: () => import('@/views/users')
}

export function initDynamicRoutes () {
  // 根据二级权限 对路由规则进行动态的添加
  const currentRoutes = router.options.routes
  // 从VueX中拿到菜单权限,把对应的菜单路由添加到路由表中
  const rightList = store.state.rightList
  
  console.log('rightList',rightList)
  rightList.forEach(item => { // 如果是没有子路由的话 就直接添加进去 如果有子路由的话就进入二级权限遍历
    // console.log(item, 'item-1')
    if (item.path) {
      const temp = ruleMapping[item.path]
      // 路由规则中添加元数据meta
      temp.meta = item.rights
      currentRoutes[2].children.push(temp)
    }

    item.children.forEach(item => {
      // item 二级权限
      // console.log(item, 'item-2')
      const temp = ruleMapping[item.path]
      // 将路由的功能权限中添加元数据meta
      temp.meta = item.rights
      currentRoutes[2].children.push(temp)
    })
  })
  // console.log(currentRoutes)
  router.addRoutes(currentRoutes)
}

渲染菜单列表

在菜单组件created()拿到Vuex中的rightlists,直接渲染上去就ok了,这部分就不上代码了,毕竟大家用的ui组件不同,对应的数据结构也不同。

按钮权限的处理

我的项目中暂时不需要考虑到按钮权限按钮权限是指:根据存在路由meta中的功能权限,控制按钮的显示点击

简单介绍一下实现方法,网上的都差不多。就是利用Vue的自定义指令实现,具体如下:

    <el-button
      class="btn"
      type="primary"
      round
      @click="handleNew"
      v-permission="{ action: 'add', effect: 'disabled' }"
      >新增管理员</el-button
    >
    
Vue.directive('permission', {
  inserted (el, binding) {
    // el是dom节点
    // action,effect都是自定义的变量
    const action = binding.value.action
    const effect = binding.value.effect
    // 判断 当前的路由所对应的组件中 如何判断用户是否具备action的权限
    if (router.currentRoute.meta.indexOf(action) === -1) { // 等于-1说明没找到 不具备权限
      if (effect === 'disabled') {
        el.disabled = true;
        el.classList.add('is-disabled');
      } else {
        el.parentNode.removeChild(el)
      }
    }
  }
})

接口权限

用户可能绕开按钮权限(F12删掉disabled),发送请求。我这里使用的是方案二

方案一

根据get,post,delete,put请求方法,与路由meta中的功能权限字段形成映射,在axios请求拦截中进行权限判断

// 映射
const actionMapping = {
  get: 'view',
  post: 'add',
  put: 'edit',
  delete: 'delete'
}

//拦截请求
request.interceptors.request.use((config)=>{
  let token =  localStorage.getItem('token');
  config.headers.Authorization = token;
  if (config.url !== '/token') {
    // 拿到请求对应的字段
    const action = actionMapping[config.method]
    // 判断非权限范围内的请求
    const currentRight = router.currentRoute.meta
    if (currentRight && currentRight.indexOf(action) === -1) {
      // 没有权限,返回提示,抛错
      Message.error('没有权限')
      return Promise.reject(new Error('没有权限'))
    }
  }
  return config
})

方案二

后端在需要鉴权的接口,解密出token中保留的用户角色role,例如角色管理模块的接口,都通过中间件判断role === 'admin',不是的话就返回错误信息。具体实现可见后端部分代码

到这一步前端的工作已全部完成啦🎉🎉

END

权限管理其实也没那么难,主要的工作是前后端沟通,要先确认好对应的字段才能进行接下来的工作。

希望这篇文章可以帮助到有需要的小伙伴,有问题可以在评论区留言或私信我🤞🤞

© 版权声明
THE END
喜欢就支持一下吧
点赞8 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容