Django权限控制进阶
一、一级菜单的排序
我们用字典存放菜单信息,而字典是无序的,当一级菜单过多时可能会出现乱序情况,因此需要给一级菜单排序
1.给一级菜单表的model中加一个weight权重的字段 ,权重越大越靠前
weight = models.IntegerField(default=1, verbose_name='权重')
2.应用有序字典存放菜单信息
引用:
from collections import OrderedDict
排序:
# sorted 按照权重的大小对字典的key进行排序
for i in sorted(menu_dict, key=lambda x: menu_dict[x]['weight'], reverse=True):
order_dict[i] = menu_dict[i]
二.非菜单权限的归属问题
一部分权限不是菜单权限,不在菜单栏显示;
如:
- 信息列表
- 客户列表
- 添加客户
- 编辑客户
在这个菜单中添加客户与编辑客户就不属于菜单权限,但在进入添加客户或编辑客户页面时需要客户列表展开;这就要给非菜单权限归类;将所属二级菜单权限作为其父权限
可以设计为如下结构:
- 信息列表
- 客户列表
- 添加客户
- 编辑客户
- 客户列表
在model的权限表中添加一个自关联的字段
# 二级菜单的归属,创建自关联字段
parent = models.ForeignKey('Permission', null=True, blank=True, verbose_name="二级菜单归属", on_delete=models.CASCADE)
三、路径导航(面包屑)
1、在权限中间件中设置一个列表,通过反射将其设为request的属性
setattr(request, settings.BREADCRUMB, [
{'url': '/index/', 'title': '首页'}
])
2、权限校验通过后给添加到路径导航信息中
3、自定义@register.inclusion_tag('rbac/breadcrumb.html')
4、页面中显示
引入rbac {% load rbac %}
显示路径导航栏 {% breadcrumb request %}
四、权限控制实现到按钮级别
控制:当前登录用户如果有该权限显示按钮,如果没有该权限不显示按钮
1、首先在权限表中创建一个存放url别名的字段
name = models.CharField(max_length=32, verbose_name='URL别名')
2、url中设置url别名,并用admin后台录入数据库
3、权限初始化时将URL别名也保存到权限字典中,一起存入session
4、自定义过滤器,判断前端传入的name在不在权限字典中,在就返回Turn
5、模板页面引用自定义的过滤器进行判断,返回Turn判断通过,表示该用户有该权限,就显示相应的按钮,否则就不显示
功能实现代码:
组件目录结构:
1.首先是表结构models.py:
from django.db import models # Create your models here. class Menu(models.Model):
"""菜单表 一级菜单"""
title = models.CharField(max_length=32)
icon = models.CharField(max_length=64, null=True, blank=True, verbose_name='图标')
weight = models.IntegerField(default=1, verbose_name='权重') def __str__(self):
return self.title class Permission(models.Model):
"""
权限表
可以做二级菜单的权限 menu 关联 菜单表
不可以做菜单的权限 menu=null
"""
url = models.CharField(max_length=32, verbose_name='权限')
title = models.CharField(max_length=32, verbose_name='标题')
menu = models.ForeignKey("Menu",null=True, blank=True, verbose_name="所属菜单",on_delete=models.CASCADE)
# 二级菜单的归属,创建自关联字段
parent = models.ForeignKey('Permission', null=True, blank=True, verbose_name="二级菜单归属", on_delete=models.CASCADE)
name = models.CharField(max_length=32, verbose_name='URL别名', default='customer_list') class Meta:
# 这个选项是指定,模型的复数形式是什么,比如:
# verbose_name_plural = "学校"
# 如果不指定Django会自动在模型名称后加一个’s’
verbose_name_plural = '权限表'
verbose_name = '权限' def __str__(self):
return self.title class Role(models.Model):
"""
角色表
"""
name = models.CharField(max_length=32, verbose_name='名称')
permissions = models.ManyToManyField('Permission', verbose_name='角色拥有的权限',blank=True) def __str__(self):
return self.name class User(models.Model):
"""
用户表
"""
name = models.CharField(max_length=32, verbose_name='名称')
password = models.CharField(max_length=32, verbose_name='密码')
roles = models.ManyToManyField('Role', verbose_name='用户拥有的角色',blank=True) def __str__(self):
return self.name
2.然后当用户登录成功后进行权限信息的初始化
service/permission.py
from django.conf import settings def init_permisson(request, obj):
"""
权限信息的初识化
保存权限和菜单的信息
:param request:
:param obj:
:return:
"""
# 登陆成功,保存权限的信息(可能存在创建了角色没有分配权限,有的用户拥有多个角色权限重复的要去重.distinct())
ret = obj.roles.all().filter(permissions__url__isnull=False).values('permissions__url',
'permissions__title',
'permissions__menu__title',
'permissions__menu__icon',
'permissions__menu_id',
'permissions__menu__weight',
'permissions__parent_id',
'permissions__parent__name',
'permissions__id',
'permissions__name',
).distinct() # 存放权限信息
permission_dict = {}
'''
权限的数据结构
permission_dict = {1: {
'url': '/customer/list/',
'id': 1,
'pid': None
}, 2: {
'url': '/customer/add/',
'id': 2,
'pid': 1
}, 3: {
'url': '/customer/edit/(\\d+)/',
'id': 3,
'pid': 1
}}'''
# 存放菜单信息
menu_dict = {} for item in ret:
# 将所有的权限信息添加到permission_dict
permission_dict[item['permissions__name']] = ({'url': item['permissions__url'],
'id': item['permissions__id'],
'pid': item['permissions__parent_id'],
'pname': item['permissions__parent__name'],
'title': item['permissions__title'],
}) # 构造菜单的数据结构
menu_id = item.get('permissions__menu_id') # 表示当前的权限是不做菜单的权限
if not menu_id:
continue # 可以做菜单的权限
if menu_id not in menu_dict:
menu_dict[menu_id] = {
'title': item['permissions__menu__title'], # 一级菜单标题
'icon': item['permissions__menu__icon'],
'weight': item['permissions__menu__weight'], # 权重
'children': [
{'title': item['permissions__title'], # 二级菜单标题
'url': item['permissions__url'],
'id': item['permissions__id'],
},
]
}
else:
menu_dict[menu_id]['children'].append(
{'title': item['permissions__title'], 'url': item['permissions__url'],
'id': item['permissions__id'], }) # print(permission_dict)
# print(menu_dict)
# 保留权限信息到session(因为session可以存到内存中,提高工作效率)
print(request)
request.session[settings.PERMISSION_SESSION_KEY] = permission_dict # 保存菜单信息
request.session[settings.PERMISSION_MENU_KEY] = menu_dict
3.在中间件中进行权限信息的校验
middlewares/rbac.py
import re from django.utils.deprecation import MiddlewareMixin
from django.conf import settings
from django.shortcuts import HttpResponse, redirect, reverse class RbacMiddleware(MiddlewareMixin): def process_request(self, request):
# 1.获取当前访问的url
url = request.path_info # 白名单 #(拿后面的url与i匹配,没匹配上Nnoe)
for i in settings.WHITE_LIST:
if re.match(i, url):
return # 2. 获取当前用户的权限信息
permission_dict = request.session.get(settings.PERMISSION_SESSION_KEY) # 没有登录的访问
if not permission_dict:
return redirect(reverse('login')) # 需要登录但是不需要进行权限校验的列表
for i in settings.NO_PERMISSION_LIST:
if re.match(i, url):
return # 路径导航
# 反射(设置属性)setattr(object, name, values) 给对象的属性赋值,若属性不存在,先创建再赋值。
setattr(request, settings.BREADCRUMB, [
{'url': '/index/', 'title': '首页'}
]) # for i in permission_dict:
# print('i',i, type(i)) # 注意i存入session中后会序列化为数字字符串,与之校验也要转化
## 1 <class 'str'>2 <class 'str'>3 <class 'str'>4 <class 'str'>5 <class 'str'> # 3.权限校验
for item in permission_dict.values():
if re.match(r"^{}$".format(item['url']), url):
pid = item.get('pid')
id = item.get('id')
pname = item.get('pname')
if pid:
# 表示该权限是二级菜单的子权限,有父权限要让父权限展开
# request.current_parent_id = pid
setattr(request, settings.CURRENT_MENU, pid) # permission_dict的key存入session后会json序列化为str所以pid也要变为str
# print('pid', pid, type(pid)) # pid 1 <class 'int'> p_dict = permission_dict[str(pid)] # 获取父权限信息
# 路径导航
getattr(request, settings.BREADCRUMB).append({'url': p_dict['url'], 'title': p_dict['title']})
getattr(request, settings.BREADCRUMB).append({'url': item['url'], 'title': item['title']}) else:
# 表示当前访问的权限是父权限, 要让自己展开
# request.current_parent_id = id
setattr(request, settings.CURRENT_MENU, id) # 路径导航
getattr(request, settings.BREADCRUMB).append({'url': item['url'], 'title': item['title']}) return
# 拒绝访问
return HttpResponse("没有访问权限 ")
4.自定义过滤器,inclusion_tag,控制页面数据的显示
templatetags/rbac.py
from django import template
from django.conf import settings
# 引入有序字典
from collections import OrderedDict
import re register = template.Library() # 菜单权限
@register.inclusion_tag('rbac/menu.html')
def menu(request):
menu_dict = request.session.get(settings.PERMISSION_MENU_KEY) # 因为字典是无序的,要使菜单显示有序:
# 1.在model的菜单表中设置weight权重字段,
# 2.引用sorted()按权重排序倒叙,权重越大显示越靠前
# 3.将数据放到有序字典中
# sorted() 函数对所有可迭代的对象进行排序操作。
# sort 是应用在 list 上的方法,sorted 可以对所有可迭代的对象进行排序操作。 order_dict = OrderedDict()
for i in sorted(menu_dict, key=lambda x: menu_dict[x]['weight'], reverse=True):
# 复制到order_dict中
order_dict[i] = menu_dict[i]
# 取一级菜单的信息
item = order_dict[i] # 控制当前权限如果是二级菜单的子权限,菜单展开
item['class'] = 'hide'
for i in item['children']:
if i['id'] == request.current_parent_id:
# 子权限
i['class'] = 'active'
item['class'] = ''
break return {'menu_list': order_dict.values()} # 路径导航,面包屑
@register.inclusion_tag('rbac/breadcrumb.html')
def breadcrumb(request):
breadcrumb_list = getattr(request, settings.BREADCRUMB)
return {'breadcrumb_list': breadcrumb_list} # 控制权限到按钮级别
@register.filter()
def has_permission(request, name):
# 判断name是否在权限的字典中
if name in request.session.get(settings.PERMISSION_SESSION_KEY):
return True
templates/rbac/menu.html 生成菜单
<div class="multi-menu">
{% for item in menu_list %}
<div class="item">
<div class="title"><i class="fa {{ item.icon }}"></i>  {{ item.title }}</div>
<div class="body {{ item.class }}" >
{% for child in item.children %}
<a href="{{ child.url }}" class="{{ child.class }}">{{ child.title }}</a>
{% endfor %} </div>
</div>
{% endfor %} </div>
templates/rbac/breadcrumb.html 生成路径导航html片段
<ol class="breadcrumb no-radius no-margin" style="border-bottom: 1px solid #ddd;">
{% for li in breadcrumb_list %}
{% if forloop.last %}
<li class="active">{{ li.title }}</li>
{% else %}
<li><a href="{{ li.url }}"> {{ li.title }}</a></li>
{% endif %} {% endfor %} </ol>
5.模板中应用
应用菜单
{# <!--引入rbac -->#}
{% load rbac %}
{# <!--应用inclusion_tag('rbac/menu.html')-->#}
{% menu request %}
路径导航
{% breadcrumb request %}
将权限控制到按钮级别,应用过滤器
{% if request|has_permission:'customer_del' or request|has_permission:'customer_edit' %}
<td>
{% if request|has_permission:'customer_edit' %}
<a style="color: #333333;" href="{% url 'customer_edit' row.pk %}">
<i class="fa fa-edit" aria-hidden="true"></i></a>
{% endif %} {% if request|has_permission:'customer_del' %}
<a style="color: #d9534f;" href="{% url 'customer_del' row.pk %}"><i
class="fa fa-trash-o"></i></a>
{% endif %}
</td>
{% endif %}
settings中的权限相关配置:
# session中保留权限key
PERMISSION_SESSION_KEY = 'permissions'
# 保留菜单信息key
PERMISSION_MENU_KEY = 'menus'
# 白名单
WHITE_LIST = [
r'^/login/$',
r'^/reg/$',
r'^/admin/.*',
]
# 需要登录但不需要校验的权限列表
NO_PERMISSION_LIST = [
r'^/index/$',
] # 路径导航(面包屑)
BREADCRUMB = 'breadcrumb_list'
# 路径导航
CURRENT_MENU = 'current_parent_id'
Django权限控制进阶的更多相关文章
- [Python学习] Django 权限控制
本文为大家讲解 Django 框架里自带的权限模型,从理论到实战演练,带领大家了解 Django 里权限是怎么一回事. 一.主要内容 1.什么是权限管理? 2.Web 权限 3.Django 权限机制 ...
- Django 权限控制配置步骤
1.models下面添加权限控制信息: class UserProfile(models.Model): user = models.OneToOneField(User) name = models ...
- django权限控制
django 权限机制的实现: http://blog.igevin.info/posts/django-permission/
- django 权限控制精简版
视图代码: 视图代码 def index(request): return render(request,'index.html') def login(request): if request.me ...
- django权限管理(Permission)
什么是权限管理 权限管理,一般指根据系统设置的安全规则或者安全策略,用户可以访问而且只能访问自 己被授权的资源 权限管理好比如钥匙,有了钥匙就能把门打开,但是权限设置是有级别之分的,假如这个 系统有多 ...
- 一.8.django权限管理/drf权限管理
1.什么是权限管理: .权限管理,一般指根据系统设置的安全规则或者安全策略,用户可以访问而且只能访问自己被授权的资源 .权限管理好比如钥匙,有了钥匙就能把门打开,但是权限设置是有级别之分的,假如这个系 ...
- django(权限、认证)系统——第三方组件实现Object级别权限控制
在我的系列blog<Django中内置的权限控制>中明确提及到,Django默认并没有提供对Object级别的权限控制,而只是在架构上留了口子.在这篇blog中,我们探讨一个简单流行的Dj ...
- django通用权限控制框架
在web项目中根据不同的用户肯定会限制其不同的权限,利用以下模块可以满足日常几乎所有的权限控制 permission_hook.py # 自定义权限控制,必须返回True/false ,True表 ...
- python 全栈开发,Day108(客户管理之权限控制,客户管理之动态"一级"菜单,其他应用使用rbac组件,django static文件的引入方式)
一.客户管理之权限控制 昨天的作业,有很多不完善的地方 下载代码,基本实现权限验证 https://github.com/987334176/luffy_permission/archive/v1.2 ...
随机推荐
- 初学Python--列表(List)
1.索引 列表中的元素类型未必统一,如: listExample=[1,2,'a','b'] 元素下标索引以0开始 firstEle=listExample[0] 不能进行越界索引,但可以倒序索引 l ...
- java 高并发解决方案
对于我们开发的网站,如果网站的访问量非常大的话,那么我们就需要考虑相关的并发访问问题了.而并发问题是绝大部分的程序员头疼的问题, 但话又说回来了,既然逃避不掉,那我们就坦然面对吧~今天就让我们一起来研 ...
- 自定义ListView里面的Item的内容
我们不可能满足只是往每个item里面填字就足够,像QQ的好友列表就是一个ListView,每个Item里面有头像.名字啊.签名什么的,内容丰富.那我们要怎么定义一个内容丰富的item呢? 要用到Ada ...
- Ubuntu 17.04 允许使用root ssh登录
用ubuntu 17.04部署完docker后,用winscp去管理系统上的文件发现默认的管理员账号权限不够,想重新用root登录,发现一只被服务器拒绝(permission denied).已经执行 ...
- JAVA基础知识总结17(网络编程)
端口: 物理端口:IP地址 逻辑端口:用于标识进程的逻辑地址,不同进程的标识:有效端口:0~65535,其中0~1024系统使用或保留端口. JAVA中ip对象:InetAddress. import ...
- eclipse egit(远程仓库)
Git的强大之一体现在远程仓库,Git是分布式版本控制系统,同一个Git仓库,可以分布到不同的机器上.怎么分布呢?最早,肯定只有一台机器有一个原始版本库,此后,别的机器可以“克隆”这个原始版本库,而且 ...
- 【转】LVS/Nginx如何处理session问题
原文地址:http://network.51cto.com/art/201005/200279.htm 通过设置persistence的值,使session会话保持. [51CTO.com独家特稿]业 ...
- OpenCV 2.4.13 installed in Ubuntu 14 and CMakeLists Demo
1. 配置编译器环境 [compiler] sudo apt-get install build-essential 2. 安装OpenCV的依赖包 [required] -dev pkg-confi ...
- Java web 中的HttpServletRequest对象
一.HttpServletRequest介绍 HttpServletRequest对象代表客户端的请求,当客户端通过HTTP协议访问服务器时,HTTP请求头中的所有信息都封装在这个对象中,通过这个对象 ...
- MongoDB整理笔记の索引
MongoDB 提供了多样性的索引支持,索引信息被保存在system.indexes 中,且默认总是为_id创建索引,它的索引使用基本和MySQL 等关系型数据库一样.其实可以这样说说,索引是凌驾于数 ...