Django-权限管理与路径导航
1、url权限管理
设计表
1、设计表
系统一共有多少个路径;
有哪些用户使用;
用户在公司的角色;
对角色进行权限分配(什么样的角色可以访问什么样的路径);
2、往表中添加数据,分配角色权限
3、登录成功,设置session,并将该用户可以访问的url从表中取出保存在session中,
4、设置中间件rocess_request,
设置白名单放行登录和admin:
以admin开头的所有路径都放行
# 设置白名单放行
for i in ["/login/","/register/","/logout/","/code/","/admin/.*"]:
# 请求的路径能够和i匹配
# 匹配不到返回None
print(request.path)
ret = re.search(i,request.path)
if ret:
print(ret)
return None
登录认证:
# 登录认证,判断当前用户是否携带session来访问
if not request.user.username:
return redirect("login")
权限认证:
session中的路径是用户可以访问的路径,
request.path是当前访问路径,与session中的路径进行匹配,search成功return None
中间件继续往下执行,search不到ret为None,如果当前访问的路径都不在这个列表中证明该用户没有访问这个路径的权限,返回对应的错误(权限不够,无法访问)
# 权限认证
for item in request.session["permisson_list"]:
res = "^%s$"%item
ret = re.search(res,request.path)
if ret:
return None
return HttpResponse("您的权限不够不能访问该页面!!!")
2、左侧菜单栏权限分配
动态显示左侧一级菜单栏
权限表模型
# 权限表
class Permission(models.Model):
title = models.CharField(max_length=32)
url = models.CharField(max_length=128)
is_menu = models.BooleanField(default=False, verbose_name='是否是菜单')
icon = models.CharField(max_length=32, verbose_name='图标', null=True, blank=True)
class Meta:
verbose_name_plural="权限表"
verbose_name = '权限表'
def __str__(self):
return self.title

admin显示:
展示的时候直接可以编辑:

from django.contrib import admin # Register your models here.
from app01 import models
# 定义显示字段
class PermissonAdmin(admin.ModelAdmin):
list_display = ['pk','title','url','is_menu','icon']
# 展示时直接就可以编辑的字段
list_editable = ['is_menu','icon','url']
# 排序
ordering = ['pk',] class RoleAdmin(admin.ModelAdmin):
list_display = ['pk','title']
ordering = ['pk',] # 注册表
admin.site.register(models.Customer)
admin.site.register(models.Campuses)
admin.site.register(models.ClassList)
admin.site.register(models.UserInfo)
admin.site.register(models.ConsultRecord)
admin.site.register(models.Role,RoleAdmin)
admin.site.register(models.Permission,PermissonAdmin)
admin文件中配置
显示表的名字:

# 权限表
class Permission(models.Model):
title = models.CharField(max_length=32)
url = models.CharField(max_length=128)
is_menu = models.BooleanField(default=False, verbose_name='是否是菜单')
icon = models.CharField(max_length=32, verbose_name='图标', null=True, blank=True)
class Meta:
verbose_name_plural="权限表"
verbose_name = '权限表'
def __str__(self):
return self.title
model中的verbose_name_plural
具体流程:
1、获取登录用户拥有的所有权限,用户菜单栏数据(用户权限的url中有哪些是菜单栏);
2、将菜单栏的数据注入到session中;
3、不管是公有客户还是私有客户菜单栏都是通过继承的模板,所以我们只需要在模板中动态生成菜单栏即可,哪些用户有哪些菜单栏;
4、为了加强代码的复用性,我们使用自定义标签来生成左侧菜单栏,,并给标签添加选中样式;
5、自定义标签把我们事先保存在session中的菜单栏数据取出来渲染到指定页面,这里我们用的inclusion_tag("result.html");
6、在后端给选中的标签添加样式,用request.path与我们取出的菜单栏数据的url进行匹配,匹配成功表示这个url就是我们点击的标签,对应的给它添加"class":"active";
用户菜单栏数据注入:
# 判断用户输入的用户名和密码与数据库中的是否匹配
user_obj = auth.authenticate(username=username, password=password)
if user_obj and str_code.upper()==code.upper():
# 登录成功设置session
auth.login(request, user_obj)
# 获取该用户的所有权限并保存在session中,并去重
permissions = models.Permission.objects.filter(role__userinfo__username=user_obj.username).distinct()
# 将该用户可以访问的所有url和菜单栏数据都添加到session中
permisson_list = []
permisson_is_menu_list = []
for item in permissions:
# 用户权限
permisson_list.append(item.url)
# 如果路径是否是菜单栏
if item.is_menu:
# 用户菜单栏数据(用户权限的url中有哪些是菜单栏)
permisson_is_menu_list.append({
"title": item.title,
"url": item.url,
"icon":item.icon,
})
request.session["permisson_list"] = permisson_list
request.session["permisson_is_menu_list"] = permisson_is_menu_list
print("permisson_list-->",permisson_list)
print("permisson_is_menu_list-->",permisson_is_menu_list)
# permisson_list--> ['/home/', '/customers/']
# permisson_is_menu_list--> [{'title': '首页', 'url': '/home/', 'icon': 'fa fa-link'}, {'title': '公共客户信息', 'url': '/customers/', 'icon': 'fa fa-link'}]
自定义标签:
from django import template
register = template.Library()
import re
@register.inclusion_tag("result.html")
def create_label(request):
menu_list = request.session.get("permisson_is_menu_list")
# 给点击的标签添加选中样式
for item in menu_list:
# 请求路径与当前用户的菜单栏数据中的url匹配成功说明这个路径就是当前用户点击的a标签发起的请求
if re.match("^{}$".format(item["url"]),request.path):
# match 匹配开头的,匹配失败返回None,匹配成功可通过group(0)返回匹配成功的字符串
# setch 扫描整个字符串,匹配不到返回None
item["class"]="active"
break
# 将菜单栏的数据交给result页面去渲染,选然后将结果当作组件返回给调用create_label的页面
return {"menu_list":menu_list}
inclusion_tag指定的result页面:
{% for foo in menu_list %}
<li class="{{ foo.class }}" ><a href="{{ foo.url }}" ><i class="{{ foo.icon }}"></i>
<span>{{ foo.title }}</span></a></li>
{% endfor %}
模板中引入自定义标签:
<ul class="sidebar-menu" data-widget="tree">
<li class="header">HEADER</li>
<!-- Optionally, you can add icons to the links --> {% load mytag %} <!--引用自定义标签-->
{% create_label request %} <!--调用自定义标签,并将request传过去--> </ul>
3、二级菜单
二级菜单和一级菜单不同的是:

1、设计表结构添加数据
一级菜单与权限表一对多关联,权限表设置自关联
# 菜单栏
class Menu(models.Model):
title = models.CharField(max_length=32,verbose_name='一级菜单',null=True,blank=True)
icon = models.CharField(max_length=32, verbose_name='图标', null=True, blank=True) # 权限表
class Permission(models.Model):
title = models.CharField(max_length=32)
url = models.CharField(max_length=128)
menu = models.ForeignKey("Menu",null=True)
pid = models.ForeignKey("self",null=True) # 自关联
class Meta:
verbose_name_plural="权限表"
verbose_name = '权限表'
def __str__(self):
return self.title


2、登陆成功后获取当前登录用户的所有权限,将用户所有可以访问的url和菜单栏数据保存在session中
# 获取该用户的所有权限并保存在session中,并去重
permissions = models.Permission.objects.filter(role__userinfo__username=user_obj.username).values("title","url","pk","pid","menu__title","menu__icon","menu_id").distinct()
# 将该用户可以访问的所有url和菜单栏数据都添加到session中
input_permission(request, permissions)
def input_permission(request, permissions):
# 将该用户可以访问的所有url和菜单栏数据都添加到session中
permisson_list = []
permisson_is_menu_dict = {}
for item in permissions:
# 用户权限
permisson_list.append({
"pk":item["pk"],
"url":item["url"],
"pid":item["pid"],
})
# 判断是否是一级菜单
if item["menu_id"]:
# menu_id同属于一个 一级菜单(下一个a标签的menu_id也是当前的一级标签)
if item["menu_id"] in permisson_is_menu_dict:
permisson_is_menu_dict[item["menu_id"]]["children"].append({
"pk":item["pk"],"title": item["title"], "url": item["url"]
})
else:
# 设计菜单栏数据结构(方便前端渲染)
permisson_is_menu_dict[item["menu_id"]] = {
"title": item["menu__title"],
"icon": item["menu__icon"],
"children": [
{ "pk":item["pk"],"title": item["title"], "url": item["url"]},
]
} request.session["permisson_list"] = permisson_list
request.session["permisson_is_menu_dict"] = permisson_is_menu_dict
中间件取提取权限列表进行验证,并将当前访问路径的pid封装在request中
import re
from django.shortcuts import HttpResponse,redirect,render
from django.utils.deprecation import MiddlewareMixin class PermissonMiddleware(MiddlewareMixin):
def process_request(self,request):
# 设置白名单放行
for i in ["/login/","/register/","/logout/","/code/","/admin/.*"]:
# 请求的路径能够和i匹配
# 匹配不到返回None
ret = re.search(i,request.path)
if ret:
return None
# 登录认证,判断当前用户是否携带session来访问
if not request.user.username:
return redirect("login") # 权限认证
for item in request.session["permisson_list"]:
res = "^%s$"%item["url"]
ret = re.search(res,request.path)
if ret:
# 当前访问路径的pid 给request对象动态添加了个属性
request.show_id = item["pid"]
return None
return HttpResponse("您的权限不够不能访问该页面!!!")
3、菜单栏的数据我们通过自定义标签来渲染
根据我们构建的数据格式在前端进行渲染
'': {
'title': '信息管理',
'icon': 'fa fa-link',
'children': [{
'pk': 5,
'title': '首页',
'url': '/home/'
}, {
'pk': 6,
'title': '公共客户信息',
'url': '/customers/'
}, {
'pk': 7,
'title': '我的客户',
'url': '/my_customers/'
}]
},
自定义标签
from django import template
register = template.Library() @register.inclusion_tag("result.html")
def create_label(request):
menu_dict = request.session.get("permisson_is_menu_dict")
for key, item in menu_dict.items():
# 默认给二级标签添加隐藏样式
item["class"] = "hide"
for child in item["children"]:
# if re.match("^{}$".format(child["url"]), request.path):
# 当前访问路径的pid如果等于它二级菜单的id证明他俩是属于同一个一级菜单,
# 如果是属于同一个一级菜单,我们就让这个二级菜单一直显示,并给它添加选中样式;
if request.show_id == child["pk"]:
item["class"] = ""
child["class"] = "active"
break
return {"menu_dict" :menu_dict }

我们通过权限表自关联的方式来区分他们的从属关系;
bug:通过正则匹配无法确定从属关系


那么我们为什么要区分他们的从属关系呢:
默认二级标签是隐藏状态的,我们为了在点击我的客户或当前一级标签的其他二级标签时都是展开的状态,我们通过正则当前请求的路径request.path与二级菜单进行匹配,匹配成功证明当前用户请求的url在二级菜单中,我们设置它父级标签的class属性值为空,让其展开; 当我们点击我的客户中的其他功能时,因为这些功能的路径都不在二级菜单中,所以无法通过正则的方式进行匹配,也就是说我们在点击这些功能时左侧的菜单栏会隐藏起来,为了避免这种bug出现我们扩展了权限表添加了pid字段,并通过自关联的方式解决了bug
当前请求路径的pid与菜单栏id进行判断:

result.html生成二级标签
<div class="multi-menu">
{% for item in menu_dict.values %}
<div class="item">
<div class="title">
<i class="{{ item.icon }}"></i>{{ item.title }}
</div>
<div class="body {{ item.class }}">
{% for child in item.children %}
<div class="body">
<a href="{{ child.url }}" class="{{ child.class}}">{{ child.title }}</a>
</div>
{% endfor %}
</div>
</div>
{% endfor %}
</div>
4、按钮权限分配
用户有没有这个按钮就看看他有没有这个按钮的权限
通过过滤器判断请求的url值否在权限列表里
{% load mytag %}
{% if "/add_customers/"|haspermission:request %}
<a href="{% url "add_customers" %}" class="btn btn-primary pull-right">添加</a>
{% endif %}
@register.filter
# 分配按钮权限
def haspermission(base_url,request):
for item in request.session["permisson_list"]:
ret = "^%s$"%item["url"]
res = re.search(ret,base_url)
if res:
return True
return False
通过过滤器判断用户权限的url中有没有该按钮的url,有就说明该用户有这个按钮的权限,让其显示;反之隐藏
{% if "/edit_customers/1/"|haspermission:request or "/delete_customers/1/"|haspermission:request %}
<td>
{% if "/edit_customers/1/"|haspermission:request %}
<a href="{% url "edit_customers" customers.pk %}"
class="btn btn-primary btn-sm">编辑</a>
{% endif %}
{% if "/delete_customers/1/"|haspermission:request %}
<a href="{% url "delete_customers" customers.pk %}"
class="btn btn-danger btn-sm">删除</a>
{% endif %}
</td>
{% endif %}
5、路径导航
页面效果:

点击我的客户页面中的其他功能:基于我的客户后面添加

1、首页是默认的,无论点击哪个菜单路径导航第一个都是首页;
2、点击左侧菜单路径导航显示对应的名称,
3、点击左侧菜单栏页面中对应的功能时路径导航基于菜单栏的路径继续往后添加;
实现思路:
我们只需要判断请求的路径是不是二级菜单:
如果是二级菜单取出对应的路径title和url在前端直接渲染即可;
如果不是二级菜单那么就是二级菜单的功能请求,我们取出功能请求的title和url并需要根据已知条件查出它父级菜单的title和url,在前端渲染时放在自己的前面;
代码:
每次请求都会走一遍中间件,权限认证通过之后判断是二级菜单还是二级菜单中的功能请求;
# -*- coding: utf-8 -*-
# @Time : 2019/6/14 20:22
import re
from django.shortcuts import HttpResponse,redirect,render
from django.utils.deprecation import MiddlewareMixin
from app01 import models
class PermissonMiddleware(MiddlewareMixin):
def process_request(self,request):
# 设置白名单放行
for i in ["/login/","/register/","/logout/","/code/","/admin/.*"]:
# 请求的路径能够和i匹配
# 匹配不到返回None
ret = re.search(i,request.path)
if ret:
return None
# 登录认证,判断当前用户是否携带session来访问
if not request.user.username:
return redirect("login") # 路径导航
request.path_navigation = [{
"title":"首页",
"url":"/home/",
}]
# 权限认证
for item in request.session["permisson_list"]: res = "^%s$"%item["url"]
ret = re.search(res,request.path)
if ret:
# 当前访问路径的pid 给request对象动态添加了个属性
request.show_id = item["pid"] # pid=pk 就是菜单栏请求的url
if item["pk"]==item["pid"]:
request.path_navigation.append({
"title":item["title"],
"url":request.path,
})
else:
# 通过pid找到二级菜单的对象
obj = models.Permission.objects.filter(pk=item["pid"]).first()
li = [
# 父级权限
{
"title":obj.title,
"url":obj.url,
},
# 子权限
{
"title": item["title"],
"url": request.path,
}
]
# 迭代添加到path_navigation中
request.path_navigation.extend(li)
return None
return HttpResponse("您的权限不够不能访问该页面!!!")
在中间件中,权限认证通过之后
用bootstrap给最后一层添加灰色样式
<ol class="breadcrumb">
{% for foo in request.path_navigation %}
{% if forloop.last %}
<li class="active">{{ foo.title }}</li>
{% else %}
<li><a href="{{ foo.url }}">{{ foo.title }}</a></li>
{% endif %} {% endfor %} </ol>
Django-权限管理与路径导航的更多相关文章
- Django 权限管理(二)
权限菜单展示 1.展示的效果 实现该效果,涉及到的核心点(突破点) 1. 权限分类: (1)菜单权限 (主要用来展示的权限(url)如查看权限 url, 如上图中的每个权限都归类为菜单权限,主要用来 ...
- django权限管理(一)
权限:权限就是一个包含正则的url. Rbac 权限管理: Role-Based Access Control,基于角色的访问控制.用户通过角色与权限进行关联,一个用户可以有多个角色,一个角色可以有多 ...
- 【Python】django权限管理
参考:http://www.cnblogs.com/esperyong/ 参考:https://docs.djangoproject.com/en/1.8/topics/auth/default/#t ...
- 一.8.django权限管理/drf权限管理
1.什么是权限管理: .权限管理,一般指根据系统设置的安全规则或者安全策略,用户可以访问而且只能访问自己被授权的资源 .权限管理好比如钥匙,有了钥匙就能把门打开,但是权限设置是有级别之分的,假如这个系 ...
- SpringSecurity实现权限管理和页面导航栏动态实现
用户模块. 3 1.1 需求:获取用户名. 3 1.1.1 分析. 3 1.1.2 服务端获取用户信息. 4 1.1.3 页面获取用户信息. 5 1.2 给用户分配角色. ...
- Django 权限管理
对于Django而言,虽然自带了一些基本的通用权限限制,但现实中,可能我们更希望自己去定义业务权限划分 Django对于权限这块的部分验证方法 user = request.user user.is_ ...
- Django权限管理测试
测试内容:当我单击登录页面登录的时候页面会弹出当前用户的个人信息 当我点击提交的时候可以看到我当前用户的所有权限: 测试成功,接下来看一下后台的简单代码: class User(models.Mode ...
- django权限管理
当我们为应用创建一个Models, 在同步到数据库里,django默认给了三个权限 ,就是 add, change, delete权限. 首先,我们创建一个perm_test的project, 然后再 ...
- Django之权限管理
Django权限管理之初步完整版 项目背景:这是一个权限管理系统(给一些角色和他们的权限指URL和页面可以删除的按钮比如:增删改查) 使用到了中间件,和初始化权限,使用了admin的后台管理系统. 我 ...
随机推荐
- Java 字符串比较
1.字符串比较 compareTo() 方法用于两种方式的比较: 字符串与对象进行比较. 按字典顺序比较两个字符串. 返回值 返回值是整型,它是先比较对应字符的大小(ASCII码顺序),如果第一个字符 ...
- spring boot logback无感配置
spring boot1.5.x版本的日志配置一直有一个问题,就是不能直接通过yml配置文件进行日志文件大小进行动态和方便的配置. 怎么解决?直接在springboot项目的maven工程中的src/ ...
- python滴啊用caffe时的小坑
在使用caffe的python接口时, 如下,如果标黄的部分不加上的话,两次调用该函数,后面的会将前面的返回值覆盖掉,也就是fea1与fea2相等,但是fea1_ori会保留原来的fea1 解决方法为 ...
- interrupt分析
转载自 https://blog.csdn.net/zhangliangzi/article/details/52485319 interrupt简述 interrupt() 方法只是改变中断状态而已 ...
- 把app(apk和ipa文件)安装包放在服务器上供用户下方法
怎么把app(apk和ipa文件)安装包放在服务器上供用户下载? IIS服务器网站不能下载.apk文件的原因:IIS的默认MIME类型中没有.apk文件,所以无法下载.解决办法:给.apk格式文件添加 ...
- [转载] 关于CI,CD,jenkins
Jenkins入门(一) https://blog.csdn.net/miss1181248983/article/details/82840006
- 02_Hive安装简介
1.下载Hive安装包: 官网下载:http://hive.apache.org/downloads.html 百度云分享:https://pan.baidu.com/s/1M4LmdOXaq6T-P ...
- Django_05_模板
模板 如何向请求者返回一个漂亮的页面呢?肯定需要用到html.css,如果想要更炫的效果还要加入js,问题来了,这么一堆字段串全都写到视图中,作为HttpResponse()的参数吗?这样定义就太麻烦 ...
- 解决Ubuntu下ssh无法启动
Ubuntu ssh一直无法启动. 通过 systemctl status ssh.service 查看到的错误是 Dec 16 13:35:22 iZm5eckxl2tqyka9eoe7b3Z ...
- Ubuntu系统---EasyECD安装记录
说明:因解决Ubuntu花屏和频繁死机的问题(后来证实本人的电脑显卡驱动有问题),手残毁坏了系统,需重装.之前从未装过系统,经过三天,反复折腾装了近十次的系统,现总结如下. 第一步:Windows 系 ...