权限管理-一级菜单-二级菜单-三级菜单-路径导航和权限粒度控制到按钮级别
权限管理 RBAC
权限管理
1. 为什么要有权限?
2. 开发一套权限的组件。为什么要开发组件?
3. 权限是什么?
web 开发中 URL 约等于 权限
4. 表结构的设计
权限表
ID URL
1 /customer/list/
2 /customer/add/
用户表
ID name pwd
1 ward 123
用户和权限的关系表(多对多)
ID user_id permission_id
1 1 1
1 1 2
5. 写代码
1. 查询出用户的权限写入session
2. 读取权限信息,判断是否有权限
最初版的权限管理梳理流程
表结构
from django.db import models
class Permission(models.Model):
"""
权限表
"""
title = models.CharField(max_length=32, verbose_name=\'标题\')
url = models.CharField(max_length=32, verbose_name=\'权限\')
class Meta:
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(to=\'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(to=\'Role\', verbose_name=\'用户所拥有的角色\', blank=True)
def __str__(self):
return self.name
settings文件配置
# ###### 权限相关的配置 ######
PERMISSION_SESSION_KEY = \'permissions\'
WHITE_URL_LIST = [
r\'^/login/$\',
r\'^/logout/$\',
r\'^/reg/$\',
r\'^/admin/.*\',
]其实权限就是用户能够访问那些url,不能访问那些url,我们所做的就是将每个不同身份的人
分配不同的url
在最初用户登录的时候就查询出用户的权限。并将此次权限存入到session中
为什么要存入session中啊,为了不重复读取数据库,存到session中
我们可以配置session然后将session存到缓存中(非关系型数据库中)
这样读取的速度回很快
登录成功后如何查看当前用户的权限并将其写入到session中
from django.shortcuts import render, HttpResponse, redirect, reverse
from rbac import models
from django.conf import settings
...
user = models.User.objects.filter(name=username, password=pwd).first()
# 登录成功
# 将权限信息写入到session
# 1. 查当前登录用户拥有的权限
permission_list = user.roles.filter(permissions__url__isnull=False).values_list(
\'permissions__url\').distinct()
# for i in permission_list:
# print(i)
# 2. 将权限信息写入到session # 这里的键值我们做了全局配置
request.session[settings.PERMISSION_SESSION_KEY] = list(permission_list)
# 得到的permission_list是一个QuerySet的元组对象,因为session的存储是有数据类型限制所以转换为列表(列表中套元组)然后,该用户能够访问那些,不能访问那些,这时,我们可以将这个逻辑写在中间件这里
from django.utils.deprecation import MiddlewareMixin
from django.conf import settings
from django.shortcuts import HttpResponse
import re
class PermissionMiddleware(MiddlewareMixin):
# 每一个请求来,都会走这个钩子函数
def process_request(self, request):
# 对权限进行校验
# 1. 当前访问的URL
current_url = request.path_info
# 白名单的判断我们这里将白名单设置在了settings中,往settings中加就ok
for i in settings.WHITE_URL_LIST:
if re.match(i, current_url):
return
# 2. 获取当前用户的所有权限信息
permission_list = request.session.get(settings.PERMISSION_SESSION_KEY)
# 3. 权限的校验
print(current_url) # Django的session做了转换将元组转换成为一个列表
for item in permission_list:
url = item[0]
if re.match("^{}$".format(url), current_url):
return
else:
return HttpResponse(\'没有权限\')升级版
动态生成一级菜单
表结构的设计
from django.db import models
class Permission(models.Model):
"""
权限表
"""
title = models.CharField(max_length=32, verbose_name=\'标题\')
url = models.CharField(max_length=32, verbose_name=\'权限\')
# 用来判断哪些url是菜单,哪些不是菜单
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
class Role(models.Model):
name = models.CharField(max_length=32, verbose_name=\'角色名称\')
permissions = models.ManyToManyField(to=\'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(to=\'Role\', verbose_name=\'用户所拥有的角色\', blank=True)
def __str__(self):
return self.name
注册层成功之后:
user = models.User.objects.filter(name=username, password=pwd).first()
# 将权限信息写入到session中
init_permission(request, user)def init_permission(request, user):
# 1. 查当前登录用户拥有的权限
permission_query = user.roles.filter(permissions__url__isnull=False).values(
\'permissions__url\',
\'permissions__is_menu\',
\'permissions__icon\',
\'permissions__title\'
).distinct()
print(\'permission_query\', permission_query)
# 存放权限信息
permission_list = []
# 存放菜单信息
menu_list = []
for item in permission_query:
permission_list.append({\'url\': item[\'permissions__url\']})
if item.get(\'permissions__is_menu\'): # 如若菜单这个字段为True
# 将这个菜单的信息先存入一个字典,然后存入session
menu_list.append({
\'url\': item[\'permissions__url\'], # 权限信息
\'icon\': item[\'permissions__icon\'], # 图标(Bootstrap的类样式)
\'title\': item[\'permissions__title\'], # 标题
})
# 2. 将权限信息写入到session
request.session[settings.PERMISSION_SESSION_KEY] = permission_list
# 将菜单的信息写入到session中
request.session[settings.MENU_SESSION_KEY] = menu_list母版中的菜单(一级菜单)
在母版中合适的位置导入这个include_tag
{% load rbac %}
{% menu request %}在templatetags下的rbac.py文件中写(自定义过滤器)
import re
from django import template
from django.conf import settings
register = template.Library()
在templates下的rbac文件夹下创建enum.html
<div class="static-menu">
{% for item in menu_list %}
<a href="{{ item.url }}" class="{{ item.class }}">
<span class="icon-wrap"><i class="fa {{ item.icon }}"></i></span>{{ item.title }}</a>
{% endfor %}
</div>
<--这个代码的样式可以放到该app文件夹下的static下的css中建立一个menu.css-->因为将数据存入了session中,所以我们可以通过request.session.来获取数据
.left-menu .menu-body .static-menu {
}
.left-menu .menu-body .static-menu .icon-wrap {
width: 20px;
display: inline-block;
text-align: center;
}
.left-menu .menu-body .static-menu a {
text-decoration: none;
padding: 8px 15px;
border-bottom: 1px solid #ccc;
color: #333;
display: block;
background: #efefef;
background:settings的配置
# ###### 权限相关的配置 ######
PERMISSION_SESSION_KEY = \'permissions\'
MENU_SESSION_KEY = \'menus\'
WHITE_URL_LIST = [
r\'^/login/$\',
r\'^/logout/$\',
r\'^/reg/$\',
r\'^/admin/.*\',
]
中间件的配置
在middlewares目录(中间件目录中)创建rbac.py文件
from django.utils.deprecation import MiddlewareMixin
from django.conf import settings
from django.shortcuts import HttpResponse
import re
class PermissionMiddleware(MiddlewareMixin):
def process_request(self, request):
# 对权限进行校验
# 1. 当前访问的URL
current_url = request.path_info
# 白名单的判断(settings中配置好了)
for i in settings.WHITE_URL_LIST:
if re.match(i, current_url):
return
# 2. 获取当前用户的所有权限信息
permission_list = request.session.get(settings.PERMISSION_SESSION_KEY)
# 3. 权限的校验
for item in permission_list:
url = item[\'url\']
if re.match("^{}$".format(url), current_url):
return
else:
return HttpResponse(\'没有权限\')
应用rbac组件
1、拷贝rbac组件到新的项目中并注册APP
2、配置权限的相关信息
# ###### 权限相关的配置 ######
PERMISSION_SESSION_KEY = \'permissions\'
MENU_SESSION_KEY = \'menus\'
WHITE_URL_LIST = [
r\'^/login/$\',
r\'^/logout/$\',
r\'^/reg/$\',
r\'^/admin/.*\',
]3、创建跟权限相关的表
执行命令
python3 manage.py makemigrations
python3 manage.py migrate
4、录入权限信息
创建超级用户
录入所有权限信息
创建角色 给角色分权限
创建用户 给用户分角色
5、在登录成功之后 写入权限和菜单的信息到session中
6、配置上中间件,进行权限的校验
7、使用动态菜单
<!-导入静态文件-->
<link rel="stylesheet" href="{% static \'css/menu.css\' %}">
使用inclusion_tag
<div class="left-menu">
<div class="menu-body">
{% load rbac %}
{% menu request %}
</div>
</div>
母版中的菜单(动态生成二级菜单)
信息管理
客户列表
财务管理
缴费列表
User name pwd
Role name permissions(FK) 2user
Permission title(二) url menu(FK) 2role
Menu title(一)
Models.py
from django.db import models
class Menu(models.Model):
"""
一级菜单
"""
title = models.CharField(max_length=32, unique=True) # 一级菜单的名字
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
class Permission(models.Model):
"""
权限表
有关联Menu的二级菜单
没有关联Menu的不是二级菜单,是不可以做菜单的权限
"""
title = models.CharField(max_length=32, verbose_name=\'标题\')
url = models.CharField(max_length=32, verbose_name=\'权限\')
menu = models.ForeignKey(\'Menu\', null=True, blank=True)
class Meta:
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(to=\'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(to=\'Role\', verbose_name=\'用户所拥有的角色\', blank=True)
def __str__(self):
return self.name
登录
from django.shortcuts import render, HttpResponse, redirect, reverse
from rbac import models
from django.conf import settings
import copy
from rbac.server.init_permission import init_permission
def login(request):
if request.method == \'POST\':
username = request.POST.get(\'username\')
pwd = request.POST.get(\'pwd\')
user = models.User.objects.filter(name=username, password=pwd).first()
if not user:
err_msg = \'用户名或密码错误\'
return render(request, \'login.html\', {\'err_msg\': err_msg})
# 登录成功
# 将权限信息写入到session
init_permission(request, user)
return redirect(reverse(\'customer\'))
return render(request, \'login.html\')
def init_permission(request, user):
# 1. 查当前登录用户拥有的权限
permission_query = user.roles.filter(permissions__url__isnull=False).values(
\'permissions__url\',
\'permissions__title\',
\'permissions__menu_id\',
\'permissions__menu__title\',
\'permissions__menu__icon\',
).distinct()
print(permission_query)
# 存放权限信息
permission_list = []
# 存放菜单信息
menu_dict = {}
for item in permission_query:
permission_list.append({\'url\': item[\'permissions__url\']})
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\'],
\'children\': [
{
\'title\': item[\'permissions__title\'],
\'url\': item[\'permissions__url\']}
]
}
else:
menu_dict[menu_id][\'children\'].append(
{\'title\': item[\'permissions__title\'], \'url\': item[\'permissions__url\']})
# 2. 将权限信息写入到session
request.session[settings.PERMISSION_SESSION_KEY] = permission_list
# 将菜单的信息写入到session中
request.session[settings.MENU_SESSION_KEY] = menu_dict
将拿到的数据存入session
写在一个自定义inclusion_tag
母版
{% load rbac %}
{% menu request %}rbac.py
import re
from django import template
from django.conf import settings
register = template.Library()
@register.inclusion_tag(\'rbac/menu.html\')
def menu(request):
menu_list = request.session.get(settings.MENU_SESSION_KEY)
return {"menu_list": menu_list}
menu.html
<div class="multi-menu">
{% for item in menu_list.values %}
<div class="item">
<div class="title"><i class="fa {{ item.icon }}"></i> {{ item.title }}</div>
<div class="body hide">
{% for child in item.children %}
<a href="{{ child.url }}">{{ child.title }}</a>
{% endfor %}
</div>
</div>
{% endfor %}
</div>menu.css0
.static-menu .icon-wrap {
width: 20px;
display: inline-block;
text-align: center;
}
.static-menu a {
text-decoration: none;
padding: 8px 15px;
border-bottom: 1px solid #ccc;
color: #333;
display: block;
background: #efefef;
background: