基于Django框架的权限组件rbac实例讲解
作者:Mr_Bai 发布时间:2022-09-27 17:11:51
1.基于rbac的权限管理
RBAC(Role-Based Access Control,基于角色的访问控制),就是用户通过角色与权限进行关联。简单地说,一个用户拥有若干角色,一个角色拥有若干权限。这样,就构造成“用户-角色-权限”的授权模型。在这种模型中,用户与角色之间,角色与权限之间都是多对多的关系。
简单的模型图示如下:
2.Rbac组件的基本目录结构:
3.按照写的流程,来讲解rbac组件中的各个部分,以及功能,
3.1 models数据库表设计(models.py)。
为了在前端页面实现2方面的控制,还需要引入两个表菜单menu和分组group:1.在一个页面,当前用户的权限,例如是否显示添加按钮、编辑、删除等按钮;2.左侧菜单栏的创建。所以一共是5个类,7张表,详细model请看下边代码。
models.py
#models.py
from django.db import models
class Menu(models.Model):
'''页面中的菜单名'''
title = models.CharField(max_length=32)
class Group(models.Model):
'''权限url所属的组'''
caption = models.CharField(verbose_name='组名称',max_length=32)
menu =models.ForeignKey(verbose_name='组所属菜单',to='Menu',default=1) # 组所在的菜单
class Meta:
verbose_name_plural = 'Group组表'
def __str__(self):
return self.caption
class User(models.Model):
"""
用户表
"""
username = models.CharField(verbose_name='用户名',max_length=32)
password = models.CharField(verbose_name='密码',max_length=64)
email = models.CharField(verbose_name='邮箱',max_length=32)
roles = models.ManyToManyField(verbose_name='具有的所有角色',to="Role",blank=True)
class Meta:
verbose_name_plural = "用户表"
def __str__(self):
return self.username
class Role(models.Model):
"""
角色表
"""
title = models.CharField(max_length=32)
permissions = models.ManyToManyField(verbose_name='具有的所有权限',to='Permission',blank=True)
class Meta:
verbose_name_plural = "角色表"
def __str__(self):
return self.title
class Permission(models.Model):
"""
权限表
"""
title = models.CharField(verbose_name='标题',max_length=32)
url = models.CharField(verbose_name="含正则URL",max_length=64)
is_menu = models.BooleanField(verbose_name="是否是菜单")
code = models.CharField(verbose_name='url代码',max_length=32,default=0) # 路径对应的描述名称
group = models.ForeignKey(verbose_name='所属组',to='Group',null=True,blank=True) # 所属组
class Meta:
verbose_name_plural = "权限表"
def __str__(self):
return self.titlemodel
3.2 service中的init_permission.py
功能:在用户登录成功的时候,在session中写入两个内容:1.拿到当前用户的权限url(code信息);2.拿到当前用户的可以做菜单的url信息。
详细代码如下:
初始化权限
def init_permission(user, request):
'''
前端页面调用,把当前登录用户的权限放到session中,request参数指前端传入的当前当前login请求时的request
:param user: 当前登录用户
:param request: 当前请求
:return: None
'''
# 拿到当前用户的权限信息
permission_url_list = user.roles.values('permissions__group_id',
'permissions__code',
'permissions__url',
'permissions__group__menu__id', # 菜单需要
'permissions__group__menu__title', # 菜单需要
'permissions__title', # 菜单需要
'permissions__url', # 菜单需要
'permissions__is_menu', # 菜单需要
).distinct()
# 页面显示权限相关,用到了权限的分组,
dest_dic = {}
for each in permission_url_list:
if each['permissions__group_id'] in dest_dic:
dest_dic[each['permissions__group_id']]['code'].append(each['permissions__code'])
dest_dic[each['permissions__group_id']]['per_url'].append(each['permissions__url'])
else:
# 刚循环,先创建需要的结构,并把第一次的值放进去。
dest_dic[each['permissions__group_id']] = {'code': [each['permissions__code'], ],
'per_url': [each['permissions__url'], ]}
request.session['permission_url_list'] = dest_dic
# 页面菜单相关
# 1.去掉不做菜单的url,拿到的结果是menu_list,列表中的元素是字典
menu_list = []
for item_dic in permission_url_list:
if item_dic['permissions__is_menu']:
temp = {'menu_id':item_dic['permissions__group__menu__id'],
'menu_title':item_dic['permissions__group__menu__title'],
'permission__title': item_dic['permissions__title'],
'permission_url':item_dic['permissions__url'],
'permissions__is_menu':item_dic['permissions__is_menu'],
'active':False, # 用于页面是否被选中,
}
# temp 其实只是给key重新起名字,之前的名字太长了。。。。
menu_list.append(temp)
# 执行完成之后是如下的数据,用来做菜单。
request.session['permission_menu_list'] = menu_list
3.3 中间件md
功能:1.白名单验证;
2.验证是否已经写入session,即:是否已经登录;
3.当前访问的url与当前用户的权限url进行匹配验证,并在request中写入code信息,
详细代码如下:
中间件
import re
from django.shortcuts import render,redirect,HttpResponse
from django.conf import settings
class MiddlewareMixin(object):
def __init__(self, get_response=None):
self.get_response = get_response
super(MiddlewareMixin, self).__init__()
def __call__(self, request):
response = None
if hasattr(self, 'process_request'):
response = self.process_request(request)
if not response:
response = self.get_response(request)
if hasattr(self, 'process_response'):
response = self.process_response(request, response)
return response
class M1(MiddlewareMixin):
'''
判断用户有无此url的权限的中间件
'''
def process_request(self,request):
current_url = request.path_info
# 1.白名单验证
valid_url = settings.VALID_URL
for each in valid_url:
if re.match(each, current_url):
return None
# 2.验证是否已经写入session,即:是否已经登录
permission_dic = request.session.get('permission_url_list')
if not permission_dic:
return redirect('/login/')
# 3.与当前访问的url与权限url进行匹配验证,并在request中写入code信息,
flag = False
for group_id,code_urls in permission_dic.items():
for url in code_urls['per_url']:
regax = '^{0}$'.format(url)
if re.match(regax,current_url):
flag = True
request.permission_code_list = code_urls['code'] # 在session中增加code的信息,用于在页面判断在当前页面的权限,
break
if flag:
break
if not flag:
return HttpResponse('无权访问')
def process_response(self,request,response):
return response
3.4 左侧菜单的生成templatetags目录下的rbac.py
功能;生成页面中的左侧菜单用inclusion_tag标签
运用:我们只需要在需要用到的文件中引用就可以生成这个菜单部分的内容。
需要用到的模板文件中:
{% load rbac %}
{% menu_html request %} 这部分就会变成用inclusion_tag生成的menu_html
详细代码如下:
inclusion_tag生成左侧菜单
import re
from django.template import Library
register = Library()
# inclusion_tag的结果是:把menu_html函数的返回值,放到menu_html中做渲染,生成一个渲染之后的大字符串,
# 在前端需要显示这个字符串的地方,只要调用menu_html就可以,如果有菜单需要传参数,这里是request,前端模板本来就有request,
@register.inclusion_tag('menu.html')
def menu_html(request):
current_url = request.path_info
# 结构化在页面显示的menu数据
menu_list = request.session.get('permission_menu_list')
menu_show_dic = {}
for item in menu_list:
# 先跟当前url进行匹配,如果当前的url在权限URl中,则需要修改当前的active,用于在前端页面的显示。
url = item['permission_url']
reg = '^{0}$'.format(url)
if re.match(reg, current_url):
print('匹配到了')
item['active'] = True
if item['menu_id'] in menu_show_dic:
menu_show_dic[item['menu_id']]['children'].append(
{'permission__title': item['permission__title'], 'permission_url': item['permission_url'],
'active': item['active']})
if item['active']:
menu_show_dic[item['menu_id']]['active'] = True
else:
menu_show_dic[item['menu_id']] = {'menu_id': item['menu_id'],
'menu_title': item['menu_title'],
'active': False,
'children': [{'permission__title': item['permission__title'],
'permission_url': item['permission_url'],
'active': item['active']}, ]
}
if item['active']:
menu_show_dic[item['menu_id']]['active'] = True
return {'menu_dic':menu_show_dic}
需要的模板文件templates下的menu.html
menu.html
# menu.html
<div class="menu">
{% for k,menu in menu_dic.items %}
{# 一级菜单 #}
<div class="menu_first">{{ menu.menu_title }}</div>
{# 二级菜单(就是一级菜单下边的内容) #}
{% if menu.active %}
<ul class="">
{% else %}
<ul class="hide">
{% endif %}
{% for child in menu.children %}
{% if child.active %}
<li class="menu_second active"><a href="{{ child.permission_url }}" rel="external nofollow" rel="external nofollow" >{{ child.permission__title }}</a></li>
{% else %}
<li class="menu_second"><a href="{{ child.permission_url }}" rel="external nofollow" rel="external nofollow" >{{ child.permission__title }}</a></li>
{% endif %}
{% endfor %}
</ul>
{% endfor %}
</div>
使用inclusion_tag的文件示例:
inclusion_tag的使用模板文件
# 这个是django的模板文件
{% load rbac %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% block title %}模板{% endblock %}</title>
<link rel="stylesheet" href="{% static 'rbac/bootstrap-3.3.7/css/bootstrap.min.css' %}" rel="external nofollow" >
<link rel="stylesheet" href="{% static 'rbac/menu.css' %}" rel="external nofollow" >
{% block css %} {% endblock css %}
</head>
<body>
<div class="container-fluid">
<div class="row">
<div class="col-md-2 menu">
{% block menu %}
{% menu_html request %} {# 用inclusion_tag生成的menu_html #}
{% endblock menu %}
</div>
<div class="col-md-9">
{% block content %}
content
{% endblock %}
</div>
</div>
</div>
来源:https://www.cnblogs.com/fengqing89/p/8283470.html


猜你喜欢
- 常用方法浅拷贝copya = {"ilpy1": {"company": "aaa&quo
- 摘要: Portal是IT领域的新技术,是企业信息化工作的发展方向之一。本文首先介绍了Oracle Portal的定义、特点,接着阐述了po
- 今天 大白 问了一个关于CSS权重的问题:关于选择器权重的问题 。class的权重是10 标签权重是 1 。比如说 p span{} 权重是
- 前言:Selenium 支持 Web 浏览器的自动化,它提供一套测试函数,用于支持 Web 自动化测试。函数非常灵活,能够完成界面元素定位、
- stringObject.substring(start,stop) 用于提取字符串中介于两个指定下标之间的字符。start必需。一个非负的
- Python 提供了 日期和时间模块用来处理日期和时间,还可以用于格式化日期和时间等常见功能。时间间隔是以秒为单位的浮点小数。每个时间戳都以
- 查看系统帮助help contentsmysql> help contents;You asked for help about he
- Innodb:[fb]# ll -hs url_comment_*.ibd633M -rw-rw---- 1 mysql mysql 632
- 1.delete不能使自动编号返回为起始值。但是truncate能使自动增长的列的值返回为默认的种子 2.truncate只能一次清空,不能
- 任务识别用相机拍下来的答题卡,并判断最终得分(假设正确答案是B, E, A, D, B)主要步骤轮廓识别——答题卡边缘识别透视变换——提取答
- 以下代码实现了ip查询功能处理程序 import os,timedef getip(filepath):
- 提取python字符串括号中的内容一些数据按字符串保存,如str1 = '(1, 0.123) (2, 0.234)',当我
- 在Python中,生成器和函数很像,都是在运行的过程中才会去确定各种变量的值,所以在很多情况下,会导致各种各样的问题。def generat
- 描述微软开发的两个动态库存在后门允许用户查看ASP文件源程序和下载整个网站详细随IIS和Frontpage Extention server
- 0. 前言本节中,我们使用策略梯度算法解决 CartPole 问题。虽然在这个简单问题中,使用随机搜索策略和爬山算法就足
- 这个问题已经不是什么新鲜问题了,网上也有大把的教程,但大多数是授人以鱼,而不授人以渔,经过辛苦的资料收集,思考,调试,整理,我基本上已经把这
- 1.原生js操作domconst dom = getElementById(‘box')2.vue官方方法:refvue中的ref是
- 很多时候,用户可能会因为承受不了某一产品中的细节设计而放弃使用该产品,这无疑是让设计人员感很沮丧的事情,因为除去这个细节的问题外,这个产品可
- 简单介绍下SecureCRTSecureCRT是一款支持SSH(SSH1和SSH2)的终端仿真程序,简单地说是Windows下登录UNIX或
- 迭代器即可以遍历诸如列表,字典及字符串等序列对象甚至自定义对象的对象,其本质就是记录迭代对象中每个元素的位置。迭代过程从第一个元素至最后一个