Django Web开发:构建强大RBAC权限管理系统的实战指南

在这里插入图片描述


文章目录

  • 前言
  • 一、rbac 基于角色的权限管理
    • 1.acl 基于用户的权限管理
    • 2.rbac 基于角色的权限管理
  • 二、应用示例
    • 1.配置角色资源
      • a.分析表
      • b.核心逻辑
      • c.使用transfer在前端实现资源配置
      • d.页面效果
    • 2.登录时获取对应权限
      • a.员工登录
      • b.中间件
      • c.前端请求
      • d.效果图
    • 3.前端-路由守卫-页面权限
  • 三、拓展


前言

    随着软件系统的日益复杂,权限管理成为确保系统安全与数据隐私的关键。传统ACL模型虽基础但受限,而RBAC模型以其灵活性和高效性逐渐成为主流。本文将简要回顾ACL概念,并深入解析RBAC的核心机制,包括角色定义、权限分配及用户角色关联。同时,将详细阐述RBAC在实际项目中的操作实践,如配置角色资源、动态获取用户权限及前端页面权限控制等。


一、rbac 基于角色的权限管理

1.acl 基于用户的权限管理

ACL(Access Control List,访问控制列表) 基于用户的权限管理

用户表:id、name、mobile、password1 张三	18612340000	1232 李四	18631240001	1329
资源表:id、name、pid1 内容菜单 null2 用户菜单 null3 课程管理  14 课程分类  15 用户管理  2
用户资源表:userid、resid1		31		42		5

2.rbac 基于角色的权限管理

rbac基于角色的权限管理

用户表:id、name、mobile、password、roleid1 张三	18612340000	1232 李四	18631240001	1329
角色表:id、name1 编辑人员	2 推广员3 财务专员
资源表:id、name、pid、url1 内容菜单 null2 用户菜单 null3 课程管理  1  /course/4 课程分类  1  /cates/5 用户管理  2  /user/
角色资源表:userid、resid1		31		42		5

流程:
员工入职–>管理员—>添加角色、添加资源、角色配置资源、添加用户(选择角色)—>用户登录—>手机号验证码—>验证通过,通过后查询此用户对应的资源(页面资源、接口资源)—>把页面资源返回给前端—>把接口资源存入redis—>在中间件验证是否有接口权限

二、应用示例

1.配置角色资源

a.分析表

user/models.py:资源表、角色表、用户表、角色资源表、接口权限表

from django.db import models# Create your models here.
# 资源表
class ResourceModel(models.Model):name = models.CharField(max_length=10, unique=True, default="默认模块")pid = models.ForeignKey('self', on_delete=models.SET_NULL, null=True, blank=True, related_name='son')url = models.CharField(max_length=20, unique=True)def __str__(self):return self.nameclass Meta:verbose_name = "资源表"verbose_name_plural = "资源表"db_table = 'resource'# 角色表
class RoleModel(models.Model):name = models.CharField(max_length=10,unique=True ,default="角色")resource = models.ManyToManyField(ResourceModel)def __str__(self):return self.nameclass Meta:verbose_name = "角色表"verbose_name_plural = "角色表"db_table = 'role'# 用户表
class UserModel(models.Model):username = models.CharField(max_length=10, default='默认用户名')phone = models.CharField(max_length=11, unique=True)password = models.CharField(max_length=10, default='123')role = models.ForeignKey(RoleModel, on_delete=models.CASCADE,default=2)def __str__(self):return self.usernameclass Meta:verbose_name = "用户表"verbose_name_plural = "用户表"db_table = 'user'# 接口权限表:
class InterfacePerModel(models.Model):resource = models.ManyToManyField(ResourceModel,related_name="interfaces")url = models.CharField(max_length=20, unique=True)def __str__(self):return self.urlclass Meta:verbose_name = '接口权限表'verbose_name_plural = '接口权限表'db_table = 'interface_per'

b.核心逻辑

# 获取角色对应信息
class RoleView(APIView):def get(self, request):data = RoleModel.objects.all()roleSer = RoleSerializer(data, many=True)return Response({"message":"获取角色资源对应信息","code":200,"data":roleSer.data})class ResourceView(APIView):def get(self, request):# 拿 roleid# 拿 roleid 对应的资源,roleid = request.GET.get('roleid')res = ResourceModel.objects.filter(pid_id__gt=0).all()# transf -- key -- labelallres = [{"key":i.id,"label":i.name} for i in res] #pid>0,所有资源role = RoleModel.objects.filter(id=roleid).first()checkres = role.resource.all() #拿这个角色对应的所有资源(选中)checkedids = [i.id for i in checkres] #拿对应的所有资源id(选中)return Response({"code":200,"allres":allres,"checkedids":checkedids})def post(self, request):resids = request.data.get("resids")roleid = request.data.get("roleid")role = RoleModel.objects.filter(id=roleid).first()print(role.resource.all())if role.resource.all():role.resource.clear()#清除之前存在的所有资源role.resource.add(*resids)#添加传进来的 资源 idsreturn Response({"code":200})

c.使用transfer在前端实现资源配置

使用 el-transfer 构建角色资源配置页面

<template><el-table :data="tableData" style="width: 100%"><el-table-column prop="name" label="角色名" width="120" /><el-table-column fixed="right" label="Operations" width="120"><template #default="scope"><el-button link type="primary" size="small" @click="handleClick">Detail</el-button><el-button link type="primary" size="small">Edit</el-button><el-button link type="primary" size="small" @click="setres(scope.row.id)">资源配制</el-button></template></el-table-column></el-table><el-dialog v-model="dialogVisible" title="Tips" width="80%"><el-transfer v-model="value" :data="data" /><template #footer><span class="dialog-footer"><el-button @click="dialogVisible = false">Cancel</el-button><el-button type="primary" @click="addresource">Confirm</el-button></span></template></el-dialog></template><script setup>
import http from "../../http";
import {onMounted,ref} from 'vue'const dialogVisible=ref(false)
const value=ref([])
const data = ref([])
const roleid = ref('')const handleClick = () => {console.log('click')
}const tableData = ref([])
onMounted(()=>{http.get('role/').then(res=>{tableData.value = res.data.dataconsole.log(res.data.data);})
})const setres=(rid)=>{roleid.value = ridhttp.get('resource/?roleid='+rid).then(res=>{data.value = res.data.allresvalue.value = res.data.checkedidsdialogVisible.value=trueconsole.log(res.data.allres);console.log(res.data.checkedids);})}const addresource=()=>{http.post('resource/',{'roleid':roleid.value,'resids':value.value}).then(res=>{roleid.value=''dialogVisible.value=false})
}
</script>

d.页面效果

在这里插入图片描述

2.登录时获取对应权限

a.员工登录

课程模块分类管理课程管理
接口权限表:资源id(manytomany)、接口地址2				/cates/3				/courses/3				/cates/

手机号—>验证通过后获取roleid,通过roleid获取资源列表,把资源列表返回给前端
eg:[{"id":1,"name":"课程模块","sons":[{"id":2,"name":"分类管理",'url':""}]}]
查询资源对应的接口权限列表,存入redis

# 账号密码登录
class LoginView(APIView):def post(self, request):# 获取参数# username = request.data.get('username', None)# password = request.data.get('password', None)phone = request.data.get('phone', None)# 验证码# ...# 查询user表print(phone)user = UserModel.objects.filter(phone__exact=phone).first()reso = user.role.resource.all()# 构建父类资源/资源信息重组、rlist = []idlist = []interlist=[] #接口权限列表for i in reso:# 获取此资源的所有接口interfaces = i.interfaces.all()for interface in interfaces:interlist.append(interface.url)pid = i.pid.idif pid not in idlist:rlist.append({"id":pid,"name":i.pid.name,'son':[]})idlist.append(pid)# 遍历rlistfor index,i in enumerate(rlist):#遍历resfor j in reso:# 找到父类的sonif j.pid.id == i['id']:rlist[index]['son'].append({"id":j.id,'name':j.name,'url':j.url})print("rlist--->")print(rlist)# 查询资源对应的接口权限列表,存入redisr.set_str('user'+str(user.id)+'interface',json.dumps(interlist))# if not user:#     return Response({"code":"2001",'message': 'User not found'})# if password != user.password:#     return Response({"code":"2002",'message': 'Wrong password'})token = mjwt.jwt_encode({"userid":user.id,'exp':int(time.time())+3600})return Response({"code":"200",'message': 'Successfully logged in',"token":token,"rlist":rlist})

b.中间件

中间件:
token 是否存在、是否过期、是否退出
从token中解析出userid,根据userid查询接口权限列表,查询redis
request.path not in reslist:

class PermitionMiddleware(MiddlewareMixin):def process_request(self, request):# 1.定义白名单,在登录前需要操作的接口放到白名单中wlist = ['/register/', '/login/', '/sendsms/']# 获取当前的urlpath = request.pathprint("middleware---------------------------1")print(path)# 2.如果不在白名单,获取token,验证if path not in wlist:print("middleware----------------------------2")try:token = request.headers.get('Authorization')data = mjwt.jwt_decode(token)except:return JsonResponse({"code": 401, "mes": "token不存在或者被修改"})# data没问题 ↓exp = int(data['exp'])now = int(time.time())userid = data['user_id']request.userid = userid# 判断是否过期if now > exp:return JsonResponse({"code": 401, "mes": "token已经过期不能操作"})#是否退出,退出时存tokenvalue = r.get_str(token)if value:return JsonResponse({"code": 401, "mes": "用户已经退出,不能操作"})# 3.↑↑↑验证是否被修改,是否过期,是否已经退出 (点击退出,把token存入redis, 加一个过期时间),任何一个问题,return 401没有权限操作,通过继续下一步操作#验证是否有权限操作接口list = r.get_str('user'+str(userid)+'interface')if list:list = json.loads(list)if path not in list:return JsonResponse({"code":401,"mes":"没有操作此接口的权限"})

c.前端请求

登录请求

    fnLogin() {// alert("登录")http.post("http://localhost:8000/login/",{phone:this.user.phone,password:this.user.password}).then((result) => {console.log(result.data.rlist);if (result.data.code == '200') {alert(result.data.message)// 资源列表localStorage.setItem('rlist',JSON.stringify(result.data.rlist))// 用户 tokenlocalStorage.setItem('token',result.data.token)// localStorage.setItem("phone",this.user.phone)// localStorage.setItem('menulist',JSON.stringify(result.data.menulist))// this.$router.push('/index')this.$router.push('/home')} else {alert(result.data.message)}}).catch((err) => {alert(err)});}

d.效果图

登录成功后,跳转到home页面,展示当前用户所具有的功能模块
List2为当前用户所配置的资源模块
在这里插入图片描述
展示用户所具有的资源模块
在这里插入图片描述

3.前端-路由守卫-页面权限

1.login接口返回menulist
2.vue页面调用登录接口
3.路由守卫

// 导航守卫
router.beforeEach((to, from, next) => {var reslist = ['/login', '/register', '/home', '/', '/chat']if (reslist.indexOf(to.path) == -1) {var token = localStorage.getItem('token')if (token) {//验证是否在权限列表中// var menulist = localStorage.getItem('menulist');var menulist = localStorage.getItem('rlist');// var mlist = JSON.parse(menulist)var mlist: any[] = menulist ? JSON.parse(menulist) : [];console.log("milist-------");console.log(mlist);if (mlist.indexOf(to.path) >= 0) {next()} else {alert("无权访问此页面")next({ "name": '/' })}} else {next({ "name": 'Login' })}}//对于登录、注册、首页等不需要权限的页面,直接放行next()})

三、拓展

  • rbac0 基础
    • 四张表
  • rbac1 角色继承
    • 基础角色–>配置资源
    • 角色->继承基础角色
    • 角色表:id、name、pid
  • rbac2 资源互斥
考试系统、比赛系统1	  考试2	  打分互斥表
res1	res21		  2
  • rbac3 继承+互斥
  • 位运算优化权限系统
    • 添加权限 | 对比权限& 删除权限^

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1488566.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

1207. 有趣的数字图形IV

问题描述 输入一个整数 &#x1d45b; &#xff08; &#x1d45b;≤12 &#xff09;&#xff0c;打印出如下要求的方阵&#xff1a; 除掉右上到左下对角线上的数外的右下半个区域中每个元素等于左边的和上面的元素之和。每个元素场宽为 5 。左上半个区域为空。 输入 一个整…

前端学习7——自学习梳理

​​​​​​jQuery 教程 | 菜鸟教程jQuery 教程 jQuery 是一个 JavaScript 库。 jQuery 极大地简化了 JavaScript 编程。 jQuery 很容易学习。 本章节的每一篇都包含了在线实例 通过本站的在线编辑器&#xff0c;你可以在线运行修改后的代码&#xff0c;并查看运行结果。 实例…

【网络】网络聊天室udp

网络聊天室udp 一、低耦合度代码1、代码2、测试结果 二、高耦合度代码1、服务端小改&#xff08;1&#xff09;维护一个unordered_map用户列表&#xff08;2&#xff09;服务端代码&#xff08;3&#xff09;客户端不改的情况下结果展示 2、大改客户端&#xff08;udp全双工用多…

stats 监控 macOS 系统

Stats 监控 macOS 系统 CPU 利用率GPU 利用率内存使用情况磁盘利用率网络使用情况电池电量 brew install stats参考 stats github

pythonGame-实现简单的贪食蛇游戏

通过python简单复现贪食蛇游戏。 使用到的库函数&#xff1a; import pygame import time import random 游戏源码&#xff1a; import pygame import time import randompygame.init()white (255, 255, 255) yellow (255, 255, 102) black (0, 0, 0) red (213, 50, 80…

封装和桥接Unity 协程体系

简介 协程&#xff08;Coroutine&#xff09;在C#中是一种特殊的函数&#xff0c;它允许开发者编写可以暂停执行并在未来某个时刻恢复执行的代码块。协程通常用于实现异步操作&#xff0c;如延时执行、等待某个事件发生、或者分段执行复杂的任务。在Unity游戏引擎中&#xff0c…

go-kratos 学习笔记(7) 服务发现服务间通信grpc调用

服务发现 Registry 接口分为两个&#xff0c;Registrar 为实例注册和反注册&#xff0c;Discovery 为服务实例列表获取 创建一个 Discoverer 服务间的通信使用的grpc&#xff0c;放到data层&#xff0c;实现的是从uses服务调用orders服务 app/users/internal/data.go 加入 New…

上传文件传参 pc端vue的formData

formData let formData new FormData(); formData.append("file", blob, ref ".png"); //添加参数并且重新命名文件名称 if(ref.toString().indexOf(qrcode) > 0) formData.append(noStbg, true)//添加参数 uploadType(formData, sour…

《人工智能大语言模型技术发展研究报告(2024)》【下载】

《人工智能大语言模型技术发展研究报告&#xff08;2024&#xff09;》下载 自2023年起&#xff0c;大模型技术产品的快速迭代和升级&#xff0c;已经成为全球科技竞争的关键因素。由中国软件评测中心发布的《人工智能大语言模型技术发展研究报告&#xff08;2024&#xff09;》…

LLM - 理解 Transformer 的位置编码 sin cos 的作用与原理

欢迎关注我的CSDN:https://spike.blog.csdn.net/ 本文地址:https://spike.blog.csdn.net/article/details/140697827 免责声明:本文来源于个人知识与公开资料,仅用于学术交流,欢迎讨论,不支持转载。 Transformer 模型中的位置编码是关键技术,通过为每个词嵌入向量添加位…

哈希表相关的力扣题和讲解和Java、C++常用的数据结构(哈希法)

20240725 一、什么时候适用什么样的结构。1.java中1.1 HashSet&#xff1a;1.2 TreeSet&#xff1a;1.3 LinkedHashSet&#xff1a;1.4 HashMap&#xff1a;1.5 TreeMap&#xff1a;1.6 LinkedHashMap&#xff1a;1.7 总结 2. c中2.1 std::unordered_set&#xff1a;2.2 std::s…

亚信安慧AntDB亮相PostgreSQL中国技术大会,获“数据库最佳应用奖”并分享数据库应用实践

7月12日&#xff0c;第13届PostgreSQL中国技术大会在杭州顺利举办&#xff0c;亚信安慧AntDB数据库荣获“数据库最佳应用奖”。大会上&#xff0c;亚信安慧AntDB数据库同事带来《基于AntDB的CRM系统全域数据库替换实践》和《亚信安慧AntDB数据库运维之路》两场精彩演讲&#xf…

vue3前端架构---打包配置

最近看到几篇vue3配置项的文章&#xff0c;转载记录一下 Vue3.2 vue/cli-service 打包 chunk-vendors.js 文件过大导致页面加载缓慢解决方案-CSDN博客文章浏览阅读2k次&#xff0c;点赞8次&#xff0c;收藏9次。Vue3.2 vue/cli-service 打包 chunk-vendors.js 文件过大导致页…

【企业级开发模型】Git分支设计模型 | 企业级项目挂历实战_准备工作开发场景实操

目录 3.Git分支设计模型 3.1master分支 3.2release分支 3.3develop分支 3.4feature分支 3.5hotfix分支 4.企业级项目挂历实战_准备工作&开发场景实操学习文档 3.Git分支设计模型 对于我们开发人员来说&#xff0c;对于不同的场景/环境&#xff0c;来设计分支模型。…

Vue3 + Vite 打包引入图片错误

1. 具体报错 报错信息 报错代码 2. 解决方法 改为import引入&#xff0c;注意src最好引用为符引入&#xff0c;不然docker部署的时候可能也会显示不了 <template><img :src"loginBg" alt""> </template><script langts setup> …

企元数智引领新零售合规分销系统免费送

企元数智近日宣布推出全新的新零售合规分销系统&#xff0c;并免费向企业提供这一创新解决方案。这一举措旨在帮助更多企业实现数字化转型&#xff0c;提高管理效率&#xff0c;促进业务增长。 新零售合规分销系统是企元数智引领的一项全新数字解决方案&#xff0c;涵盖了销售数…

华为强制恢复出厂设置后如何恢复数据?数据重生的2个方法介绍

华为作为全球知名的手机品牌&#xff0c;其产品在市场上广受欢迎。然而&#xff0c;有时由于各种原因&#xff0c;我们可能需要强制恢复出厂设置&#xff0c;这往往意味着数据的丢失。那么&#xff0c;如何在华为强制恢复出厂设置后&#xff0c;让数据“重生”呢&#xff1f;本…

通信类IEEE会议——第四届通信技术与信息科技国际学术会议(ICCTIT 2024)

[IEEE 独立出版&#xff0c;中山大学主办&#xff0c;往届均已见刊检索] 第四届通信技术与信息科技国际学术会议&#xff08;ICCTIT 2024&#xff09; 2024 4th International Conference on Communication Technology and Information Technology 重要信息 大会官网&#xf…

ETL数据集成丨将PostgreSQL数据库数据实时同步至PostgreSQL

前言 我们在进行数据集成、实时数据同步中&#xff0c;经常会出现在同一个数据库中做数据同步和复制、实时分析和报告、负载均衡和高可用性等场景&#xff0c;这次我们以PostgreSQL为例&#xff0c;通过ETLCloud工具&#xff0c;进行同数据库中数据实时同步的步骤应该如何设置…

无人机组装与操作实训课程详解

一、课程名称与目标 课程名称&#xff1a;无人机组装与操作实训课程 课程目标&#xff1a;本课程旨在培养学员对无人机组装技术的深入理解和实际操作能力&#xff0c;使学员能够独立完成无人机的组装、调试和日常维护工作&#xff0c;并具备一定的无人机操作能力和安全意识。…