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.mzph.cn/web/49087.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

GAT知识总结

《GRAPH ATTENTION NETWORKS》 解决GNN聚合邻居节点的时候没有考虑到不同的邻居节点重要性不同的问题&#xff0c;GAT借鉴了Transformer的idea&#xff0c;引入masked self-attention机制&#xff0c; 在计算图中的每个节点的表示的时候&#xff0c;会根据邻居节点特征的不同来…

解开基于大模型的Text2SQL的神秘面纱

你好&#xff0c;我是 shengjk1&#xff0c;多年大厂经验&#xff0c;努力构建 通俗易懂的、好玩的编程语言教程。 欢迎关注&#xff01;你会有如下收益&#xff1a; 了解大厂经验拥有和大厂相匹配的技术等 希望看什么&#xff0c;评论或者私信告诉我&#xff01; 文章目录 一…

JAVA基础 - 对象

目录 一. 简介 二. 空对象 三. 构造方法 四. 析构方法 五. this关键字 六. 对象销毁 一. 简介 在 Java 中&#xff0c;对象&#xff08;Object&#xff09;是面向对象编程的核心概念。 对象是类的实例化&#xff0c;它将数据&#xff08;属性&#xff09;和操作这些数据…

【运算放大器】输入失调电压和输入偏置电流(2)实例计算

概述 根据上一篇文章的理论&#xff0c;分别计算没有输入电阻和有输入电阻两种情况下的运放总输出误差。例题来自于TI高精度实验室系列课程。 目录 概述实例计算 1&#xff1a;没有输入电阻实例计算 2&#xff1a;有输入电阻总结 实例计算 1&#xff1a;没有输入电阻 要求&am…

通过IEC104转MQTT网关对接阿里云、华为云、亚马逊AWS、ThingsBoard、Ignition、Zabbix

随着工业互联网的快速发展&#xff0c;传统电力系统中的IEC 104协议设备正逐步向更加开放、灵活的物联网架构转型。MQTT&#xff08;Message Queuing Telemetry Transport&#xff09;作为一种轻量级的消息传输协议&#xff0c;因其低带宽消耗、高可靠性和广泛的支持性&#xf…

vue3前端开发-小兔鲜项目-路由拦截器增加token的携带

vue3前端开发-小兔鲜项目-路由拦截器增加token的携带&#xff01;实际开发中&#xff0c;很多业务接口的请求&#xff0c;都要求必须是登录状态&#xff01;为此&#xff0c;这个token信息就会频繁的被加入到了请求头部信息中。request请求头内既然需要频繁的携带这个token.我们…

集团ERP信息化项目实施方案(82页PPT)

集团ERP信息化项目实施方案的82页PPT详尽阐述了企业资源规划&#xff08;ERP&#xff09;系统实施的全过程&#xff0c;旨在帮助集团整合多个业务流程于一个统一的平台。方案从当前市场环境分析入手&#xff0c;解释了ERP系统对于提升集团运营效率、降低成本和优化资源配置的必…

【OpenCV C++20 学习笔记】图片融合

图片融合 原理实现结果展示完整代码 原理 关于OpenCV的配置和基础用法&#xff0c;请参阅本专栏的其他文章&#xff1a;垚武田的OpenCV合集 这里采用的图片熔合的算法来自Richard Szeliski的书《Computer Vision: Algorithms and Applications》&#xff08;《计算机视觉&#…

STM32是使用的内部时钟还是外部时钟

STM32是使用的内部时钟还是外部时钟&#xff0c;经常会有人问这个问题。 1、先了解时钟树&#xff0c;见下图&#xff1a; 2、在MDK中&#xff0c;使用的是HSEPLL作为SYSCLK&#xff0c;因此需要对时钟配置寄存器&#xff08;RCC_CFGR&#xff09;进行配置&#xff0c;寄存器内…

Eaton伊顿触摸屏维修XV-303-15-C00-A00-1C

伊顿触摸屏维修,工业触摸屏维修,主板维修,坏高故障,损坏显示,不损坏,运行稳定,不花屏,无反应慢等故障维修,维修有保障,资费低.,触摸屏主板坏,高压板故障,按键损坏等均可修理。 伊顿触摸屏维修 EATON触摸屏维修 伊顿工控机维修 EATON工控机维修 伊顿人机界面维修 EATON触摸屏维…

深度解读大语言模型中的Transformer架构

一、Transformer的诞生背景 传统的循环神经网络&#xff08;RNN&#xff09;和长短期记忆网络&#xff08;LSTM&#xff09;在处理自然语言时存在诸多局限性。RNN 由于其递归的结构&#xff0c;在处理长序列时容易出现梯度消失和梯度爆炸的问题。这导致模型难以捕捉长距离的依…

【机器学习】用Jupyter Notebook实现并探索单变量线性回归的代价函数以及遇到的一些问题

引言 在机器学习中&#xff0c;代价函数&#xff08;Cost Function&#xff09;是一个用于衡量模型预测值与实际值之间差异的函数。在监督学习中&#xff0c;代价函数是评估模型性能的关键工具&#xff0c;它可以帮助我们了解模型在训练数据上的表现&#xff0c;并通过优化过程…

数据结构——排序大汇总(建议收藏)

这篇文章将为大家详细讲解各大排序的基本思想与实现代码~ 内有动图 首先&#xff0c;我们来看常见的排序有以下几大类&#xff1a; 1.插入排序 插入排序的主要思想是将每个位置的元素插入到前面已具备顺序的数组中 实际中我们玩扑克牌时&#xff0c;就用了插入排序的思想 …

快手可灵视频生成大模型全方位测评

快手视频生成大模型“可灵”&#xff08;Kling&#xff09;&#xff0c;是全球首个真正用户可用的视频生成大模型&#xff0c;自面世以来&#xff0c;凭借其无与伦比的视频生成效果&#xff0c;在全球范围内赢得了用户的热烈追捧与高度评价。截至目前&#xff0c;申请体验其内测…

人工智能:大语言模型提示注入攻击安全风险分析报告下载

大语言模型提示注入攻击安全风险分析报告下载 今天分享的是人工智能AI研究报告&#xff1a;《大语言模型提示注入攻击安全风险分析报告》。&#xff08;报告出品方&#xff1a;大数据协同安全技术国家工程研究中心安全大脑国家新一代人工智能开放创新平台&#xff09; 研究报告…

stats 监控 macOS 系统

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

59、mysql存储过程

存储过程 一、存储过程&#xff1a; 1.1、存储过程的概念 概念&#xff1a;完成特定功能的sql语句的集合。把定义好的sql集合在一个特定的sql的函数当中 每次执行调用函数即可。还可以实现传参的调用。 1.2、存储过程的语法&#xff1a; delimiter $$ ##delimiter开始和结…

支持4K高分辨率,PixArt-Sigma最新文生图落地经验

PixArt-Sigma是由华为诺亚方舟实验室、大连理工大学和香港大学的研究人员共同开发的一个先进的文本到图像&#xff08;Text-to-Image&#xff0c;T2I&#xff09;生成模型。 PixArt-Sigma是在PixArt-alpha的基础上进一步改进的模型&#xff0c;旨在生成高质量的4K分辨率图像。…

2024牛客暑期多校第四场

A-LCT 带权并查集&#xff0c;维护一下每个点在当前树的深度和以它为根能找到的最深的深度。‘ #include<bits/stdc.h>using namespace std; typedef long long ll; const int N 1e6 100;int fa[N],ans[N],val[N];int find(int x){if(fa[x]x)return x;int tfa[x];fa[x…

C++初学(3)

面向对象编程&#xff08;OOP&#xff09;的本质是设计并拓展自己的数据类型&#xff0c;设计自己的数据类型就是让类型与数据匹配。内置的C类型分为两组&#xff1a;基本类型和复合类型。这里我们将介绍基本类型的整数和浮点数 3.1、简单变量 3.1.1、变量名 C必须遵循几种简…