權限管理css
1. 爲何要有權限?html
2. 開發一套權限的組件。爲何要開發組件?python
3. 權限是什麼?web
web 開發中 URL 約等於 權限數據庫
4. 表結構的設計django
權限表 緩存
ID URL session
1 /customer/list/app
2 /customer/add/ide
用戶表
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組件
一、拷貝rbac組件到新的項目中並註冊APP
二、配置權限的相關信息
# ###### 權限相關的配置 ######
PERMISSION_SESSION_KEY = 'permissions'
MENU_SESSION_KEY = 'menus'
WHITE_URL_LIST = [
r'^/login/$',
r'^/logout/$',
r'^/reg/$',
r'^/admin/.*',
]三、建立跟權限相關的表
執行命令
python3 manage.py makemigrations
python3 manage.py migrate
四、錄入權限信息
建立超級用戶
錄入全部權限信息
建立角色 給角色分權限
建立用戶 給用戶分角色
五、在登陸成功以後 寫入權限和菜單的信息到session中
六、配置上中間件,進行權限的校驗
七、使用動態菜單
<!-導入靜態文件-->
<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: