一個網站如果沒有權限控管,就等於是沒有保安的辦公大樓,所有人都可以任意進出任何地點使用任何資料。
但我們也希望能夠製作一個可以插拔的權限控管,可以先把主要業務邏輯做好以後,再把權限控管加入,不用做太大的更動。
目前這個組件的目錄結構是長這樣子:
rbac
├─service
│ ├─middlewares.py
│ └─rbac.py
├─template
│ └─menu.html
├─tempaletags
│ └─myilters.py
└─models.py
我學習的是rbac(Role Based Access Control)權限控制模型,也就是依照使用者角色來區分權限。
這個方式的好處就是,不必在同一個權限上重複添加使用者,而是藉由角色來套用權限,減少儲存空間的浪費。
這是ㄧ個基本的表結構,可以讓我們建立一個一級選單:
from django.db import models #用戶表 class User(models.Model): name = models.CharField(max_length=32) # password = models.CharField(max_length=32) roles = models.ManyToManyField("Role") def __str__(self): return self.name #角色表 class Role(models.Model): title = models.CharField(max_length=32) permissions = models.ManyToManyField("Permission") def __str__(self): return self.title #權限表 class Permission(models.Model): title = models.CharField(max_length=32) url = models.CharField(max_length=128) is_menu =models.BooleanField(default=False) icon = models.CharField(max_length=32) def __str__(self): return self.title
首先創建表結構,表結構包含用戶、角色、權限,還有另外兩個關係表,這兩個關係表關係如下:
一個使用者可以對應到多個角色,這些角色又有其對應權限。
User<---------多對多--------->Role
Role<---------多對多--------->Permission
在用戶登入以後,我們將該用戶所有的權限,寫到session中的權限列表中,這樣子我們未來要判斷用戶權限,只要透過session,就可以取得該用戶的所擁有的權限。
service/rbac.py
from rbac import models def initial_session(name,request): """ 功能:將當前登入人的所有權限錄入session中 """ # 查出當前用戶的所有權限列表,使用distnct()將所有重複項除去 permissions = models.User.objects.filter(name=name).values("roles__permissions__url", "roles__permissions__is_menu", "roles__permissions__title", "roles__permissions__icon" ).distinct() permission_list = [] permission_menu_list = [] for item in permissions: #將用戶權限寫入權限列表 permission_list.append(item["roles__permissions__url"]) #選單權限列表 #在寫入權限時,一併將選單權限寫入 if item["roles__permissions__is_menu"]: permission_menu_list.append({ 'title':item["roles__permissions__title"], 'icon':item["roles__permissions__icon"], 'url':item["roles__permissions__url"] }) # 將當前登錄人的權限列表寫入session中 request.session["permission_list"] = permission_list # 將當前登錄人的左側選單寫入session中 request.session["permission_menu_list"] = permission_menu_list print('permission_menu_list',permission_menu_list)
因為必須讓權限套用在全局,我們採用中間件的方式,這樣子就不用對每個視圖單獨做設置。
service/middleware.py中寫入以下程式碼:
from django.utils.deprecation import MiddlewareMixin from django.shortcuts import HttpResponse,redirect import re class PermissionMiddleWare(MiddlewareMixin): def process_request(self,request): #獲取當前路徑 current_path = request.path #設置白名單 for reg in ["/admin*","/crm/login/","/crm/validimg/","/crm/logout/"]: if re.search(reg,current_path): return None #校驗用戶是否已登入 user_id = request.session.get("user_id") if not user_id: return redirect("/crm/login/") #校驗權限 #獲取session中寫入的用戶權限列表 #如果用戶訪問的頁面不在權限列表中,禁止該用戶訪問 permission_list = request.session.get("permission_list") for reg in permission_list: reg = "^%s$" % reg ret = re.search(reg,current_path) if ret: return None return HttpResponse("無權訪問")
選單會動態生成,所以我們將選單獨立為一個menu.html文件
{% load myfilters %} {% for item in permission_menu_list %} <li class="nav-item"> <a href="{{ item.url }}" class="nav-link {{ item.active }}"> <i class="{{ item.icon }} nav-icon"></i> <p>{{item.title}}</p> </a> </li> {% endfor %}
選單有被選中或未選中的差異,在class中加入"active"來區隔。判斷上面就是當 request.path == reg_path 時,將active加入
from django.template import Library from django.utils.safestring import mark_safe import re register = Library() #自定義一個includsion標籤 @register.inclusion_tag("rbac/menu.html") def get_menu_style(request): permission_menu_list = request.session['permission_menu_list'] for item in permission_menu_list: # 使用re來判斷當前url是否在選單權限列表中 if re.search(item['url'],request.path): # 如果找到的話,在該字典中加入一個active元素,用來渲染頁面 item['active'] = 'active' return {'permission_menu_list':permission_menu_list}
二級選單:
更多時候選單會摺疊起來

這時候就會需要二級選單,這邊就不能再使用一級選單的列表資料結構,列表無法呈現多個層級的選單結構,所以要使用字典來表現。
使用字典會如同下方的格式:
permission_menu_list:
{
1:{
"title":"層級一",
"icon":"",
"children":[
{
"title":"層級二",
"url":"",
},
{
"title":"層級二",
"url":"",
},
]
},
2:{
"title":"層級一",
"icon":"",
"children":[
{
"title":"層級二",
"url":"",
},
{
"title":"層級二",
"url":"",
},
]
},
}
未完....
浙公网安备 33010602011771号