許可權管理-一級選單-二級選單-三級選單-路徑導航和許可權粒度控制到按鈕級別
許可權管理 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': [