权限管理-一级菜单-二级菜单-三级菜单-路径导航和权限粒度控制到按钮级别
权限管理 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: