RBAC用户角色权限管理模型设计(全网最全没有之一)

前言

作为一个后台管理系统,权限管理是一个绕不开的话题,一个成熟的后端系统离不开一个比较完善的权限管理系统,他是可以为我们提供更好的服务以及体验,话不多说下边我们开始步入正题。

RBAC权限简介

RBAC(Role-Based Access Control),即基于角色的访问控制,是一种广泛应用于信息安全领域的访问控制模型。RBAC的核心思想是将系统中的用户按照其角色进行分类,然后定义不同角色具有的权限,最后指定哪些用户属于哪些角色,从而实现权限管理。

RBAC 的主要组成部分包括角色、权限和用户。角色是指对某一类用户进行抽象的描述,包括他们所拥有的权限;权限是指系统的操作或资源,可以被分配给角色或用户;用户是系统中的使用者,可以被分配到一个或多个角色中。

RBAC 的优点包括简化了权限管理,降低了管理的复杂性;提高了安全性,通过限制用户的权限,减少了系统受到攻击的可能性;提高了系统的灵活性和可扩展性,可以根据需要对角色和权限进行动态调整。

前端技术栈

vue3、vite、element-plus、axios、TypeScript、pinia

后端技术栈

node、express、mongoDB

实现流程

1、后端数据库表设计
1、创建用户信息表
const usersSchema = new Schema({
    username: {
        type: String,
        default: `user_${str}`
    },//用户名
    password: String,//密码
    name: {
        type: String,
        default: `张三${str}`
    },//姓名
    role: {
        type: [mongoose.Types.ObjectId],
        ref: "roles",
        default: null
    },
    //头像
    avatar: {
        type: String,
        default: 'https://img.zcool.cn/community/01786557e4a6fa0000018c1bf080ca.png@1280w_1l_2o_100sh.png'
    }
})

2、创建角色信息表 
const rolesSchema = new Schema({
    rolename: String,
    menu: {
        type: [mongoose.Types.ObjectId],
        ref: "menu"
    },
    buttons: {
        type: [mongoose.Types.ObjectId],
        ref: "buttons"
    },
})

3、创建路由菜单表 
const menuSchema = new Schema({
    title: String,
    level: Number,
    name:{
        type:String,
        default:null
    },
    rid: {
        type: mongoose.Types.ObjectId,
        ref: "menu",
        default: null
    },
})

4、创建按钮权限表
const buttonsSchema = new Schema({
    title: String,
    rid: mongoose.Types.ObjectId,
    name:{
        type:String,
        default:null
    },
    level:{
        type:Number,
        default:null
    },
})

 2、 前端设计

1、创建并暴露静态路由表、动态路由表以及任意路由。  

静态路由表:登录页、注册页、首页等不需要认证角色的页面。

export const constantRoute = [
  // 登录
  {
    name: "login", //命名路由
    path: "/login",
    component: () => import("../views/Login.vue"),
    meta: {
      title: "登录", //菜单标题
      hidden: true, //代表路由标题在菜单中是否隐藏 true:隐藏 false:不隐藏
    },
  },
  // 登录成功以后展示数据的路由
  {
    name: "HomeView", //命名路由
    path: "/",
    component: () => import("../views/HomeView.vue"),
    meta: {
      title: "", //菜单标题
      hidden: false,
      icon: "", //图标
    },
    redirect: "/home", //让你访问跟路径时,直接重定向home页面
    children: [
      {
        name: "home",
        path: "/home",
        component: () => import("../views/Home.vue"),
        meta: {
          title: "首页", //菜单标题
          hidden: false,
          icon: "HomeFilled", //菜单文字左侧的图标,支持element-plus所以图标
        },
      },
    ],
  },
  // 404
  {
    name: "404", //命名路由
    path: "/404",
    component: () => import("../views/404.vue"),
    meta: {
      title: "404", //菜单标题
      hidden: true,
    },
  },
];

动态路由表:用户信息页、用户管理页、权限设置页等需要认证角色信息的页面。

//动态路由
export const allAsyncRoutes = [
  // 用户信息
  {
    name: "list",
    path: "/list",
    component: () => import("../views/HomeView.vue"),
    meta: {
      title: "用户管理",
      hidden: false,
      icon: "User",
    },
    redirect: "/list/about",
    children: [
      {
        name: "about",
        path: "/list/about",
        component: () => import("../views/AboutView.vue"),
        meta: {
          hidden: false,
          title: "用户列表",
          icon: "Platform",
        },
      },
    ],
  },
  // 权限管理
  {
    name: "acl",
    path: "/acl",
    component: () => import("../views/HomeView.vue"),
    meta: {
      hidden: false,
      title: "权限管理",
      icon: "Lock",
    },
    redirect: "/acl/user",
    children: [
      {
        name: "user",
        path: "/acl/user",
        component: () => import("../views/acl/user/index.vue"),
        meta: {
          hidden: false,
          title: "用户管理",
          icon: "User",
        },
      },
      {
        name: "role",
        path: "/acl/role",
        component: () => import("../views/acl/role/index.vue"),
        meta: {
          hidden: false,
          title: "角色管理",
          icon: "UserFilled",
        },
      },
      {
        name: "permission",
        path: "/acl/permission",
        component: () => import("../views/acl/permission/index.vue"),
        meta: {
          hidden: false,
          title: "菜单管理",
          icon: "Monitor",
        },
      },
    ],
  },
];

任意路由:当用户在导航栏输入错误路由时,需要跳转的页面比如404页面。

//任意路由
export const anyRoutes = {
  name: "Any",
  path: "/:pathMatch(.*)*",
  redirect: "/404",
  meta: {
    title: "任意路由", //菜单标题
    hidden: true,
  },
};
 2、利用pinia创建用户信息仓库

创建仓库用来存储后端返回用户信息的路由权限表、角色表、按钮权限表。并根据这些权限来生成最终的用户界面导航栏,以及按钮是否启用。在这里我们需要引入router实例,以及loadsh库中的clonedeep深拷贝方法,来对静态路由表进行深拷贝。还需要利用递归对动态路由已经后端返回的路由进行匹配,最终生成最新的动态路由菜单。最后对动态路由以及静态路由进行合并并重新赋值。接下来我们看一下实现代码。

// 创建用户小仓库
let useUserStore = defineStore('User', {
  // 小仓库存储数据地方
  state: () => {
    return {
      token: localStorage.getItem('roleid'), //admin
      name: '',
      roles: '',
      menuRoutes: constantRoute, //仓库存储生成菜单需要数组(路由)
      authBtnList: [], //储存按钮功能的列表
      avatar: '',
    }
  },
  // 异步 | 逻辑的地方
  actions: {
    // 获取用户信息
    async getinfo() {
      const { data } = await axios.get(
        `http://localhost:3000/userinfo?id=${this.token}`,
      )
      if (data.code === 200) {
        this.name = data.name
        this.roles = data.rolenames
        this.authBtnList = data.buttons
        this.avatar = data.avatar
        const asyncRoutes = filterAsyncRoutes(
          // 过滤路由,过滤出拥有的菜单权限
          cloneDeep(allAsyncRoutes),
          data.routes,
        )
        console.log(asyncRoutes);
        // 将路由进行拼接,过滤出来的路由以及任意路由(404)
        addRoutes([...asyncRoutes, anyRoutes])
        this.menuRoutes = [...constantRoute, ...asyncRoutes] //拼接完的在于静态路由进行拼接并赋值
        return 'ok'
      } else {
        return Promise.reject('获取用户信息失败')
      }
    },

})
//利用递归对异步路由和后台返回的路由进行匹配,最终生成动态菜单路由
function filterAsyncRoutes(allAsyncRoutes, routesName) {
  return allAsyncRoutes.filter((route) => {
    if (routesName.indexOf(route.name) === -1) {
      return false
    }
    if (route.children && route.children.length > 0) {
      //递归
      route.children = filterAsyncRoutes(route.children, routesName)
    }
    return true
  })
}
// 动态路由拼接
function addRoutes(routes) {
  console.log(routes)
  routes.forEach((route) => {
    router.addRoute(route)
  })
}
3、在完成登陆后调用pinia仓库中的方法来获取用户权限
// 获取用户相关的小仓库
import useUserStore from "../stores/modules/user";
let userStore = useUserStore();
const login = async () => {
  const { data } = await axios.get(
    `http://localhost:3000/login?username=${username.value}&&password=${password.value}`
  );
  console.log(data.data[0]._id);

  localStorage.setItem("roleid", data.data[0]._id);
  userStore.changeToken(data.data[0]._id);
  userStore.getinfo();
  router.push("/");
};
4、按钮级权限实现

按钮级权限采用自定义指令来实现,通过pinia仓库保存的按钮信息来及逆行判断是否存在该按钮权限,并采用disabled来对按钮进行禁用操作。

import useUserStore from '../stores/modules/user.js'
export const isHasButton = (app) => {
  let userStore = useUserStore()
  //全局自定义指令
  app.directive('has', {
    mounted(el, options) {
      if (!userStore.authBtnList.includes(options.value)) {
        el.parentNode.removeChild(el)
      }
    },
  })
}

以上就是RBAC权限管理的全部过程,如果小伙伴在实现时遇见问题,可以在评论区留言,我看到后会在第一时间帮助大家解决问题。如果这篇文章能够帮助到大家,也希望大家可以给小编点一个免费的小赞。 

  • 12
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值