根據URL進行限制用戶能夠訪問的資源css
項目可包含多個應用
應用可包含在多個項目中
RBAC:基於權限的管理系統html
先建立一個Django項目前端
Modeldjango
from django.db import models class UserInfo(models.Model): name = models.CharField(max_length=32) pwd = models.CharField(max_length=32,default=123) email = models.EmailField() roles = models.ManyToManyField(to="Role") def __str__(self): return self.name class Role(models.Model): title =models.CharField(max_length=32) permissions = models.ManyToManyField(to="Permission") def __str__(self): return self.title class Permission(models.Model): url = models.CharField(max_length=32) title = models.CharField(max_length=32) def __str__(self): return self.title
前端模板bootstrap
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta http-equiv="x-ua-compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Title</title> </head> <body> <form action="/login/" method="post"> {% csrf_token %} <p>用戶名<input type="text" name="user"></p> <p>密碼<input type="password" name="pwd"></p> <p><input type="submit" value="登陸"></p> </form> </body> </html>
URL後端
urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^login/', views.login), url(r'^users/', views.user_list), url(r'^orders/', views.role_list), ]
後端cookie
from django.shortcuts import render, HttpResponse, redirect from rbac.models import UserInfo, Role, Permission def login(request): if request.method == "GET": return render(request, "login.html") if request.method == "POST": username = request.POST.get("user") pwd = request.POST.get("pwd") user = UserInfo.objects.filter(name=username, pwd=pwd).first() if user: request.session["user_id"] = user.pk permission_list = user.roles.all().values("permissions__url", "permissions__title").distinct() temp = [] for per_url in permission_list: temp.append(per_url["permissions__url"]) request.session["permissions_list"] = temp print(temp) return HttpResponse("OK") else: return redirect('/login/') def user_list(request): return HttpResponse("用戶列表") def role_list(request): return HttpResponse("訂單列表")
後端有不少的視圖函數,若是編寫裝飾器進行判斷用戶是否有權限訪問,有三十個視圖函數,就須要在三十個視圖函數上添加裝飾器函數,所以裝飾器的方法不太穩當,取而代之的是中間件的方法session
from django.utils.deprecation import MiddlewareMixin #注意 from django.shortcuts import render,redirect, HttpResponse from rbac.models import UserInfo import re #注意 class M1(MiddlewareMixin): def process_request(self,request): current_path = request.path_info permission_list = request.session.get("permissions_list") print(permission_list) valid_menu = ["/login/","/reg/","/admin/.*"] # 若是不設置白名單,admin的url也會被判爲無權限,並且不須要驗證的函數少, 先設置白名單, # 若是用戶輸入的url在白名單中就會return None for valid_url in valid_menu: ret = re.match(valid_url,current_path) #注意 if ret: return None if not permission_list: return None Flage = False for per_url in permission_list: re_macth = re.match(per_url,current_path) if re_macth: Flage = True break if not Flage: return HttpResponse("無權限")
在rabc應用之service包intiale 模塊中建立一個inital_session函數,登陸後處理sessionapp
from rbac.service.initial import inital_session def login(request): if request.method == "GET": return render(request, "login.html") if request.method == "POST": username = request.POST.get("user") pwd = request.POST.get("pwd") user = UserInfo.objects.filter(name=username, pwd=pwd).first() if user: inital_session(request,user) return HttpResponse("OK") else: return redirect('/login/')
url_filter 模塊下的代碼
def inital_session(request,user): request.session["user_id"] = user.pk permission_list = user.roles.all().values("permissions__url", "permissions__title").distinct() temp = [] for per_url in permission_list: temp.append(per_url["permissions__url"]) request.session["permissions_list"] = temp
目錄結構以下圖ide
一、在項目中建立一個應用application,本身命名至於爲何?這是前面提到的:「一個應用能夠包含在多個項目中」,方便之後的使用
二、在項目中建立一個文件夾service,
三、在service 中建立一個py文件,存放本身中間件類
四、建立一個類,必須繼承 MiddlewareMixin
五、該類中必須有一個函數,process_request
六、在該文件下面建立一個inital_session 的模塊,處理登陸後,session
作好以上步驟,效果以下圖
上面的介紹如何使用中間件控制用戶的訪問那個函數,下面介紹,根據用戶的角色展現菜單
用戶登陸成功,在cookie中寫入用戶的權限
import re def inital_session(request, user): request.session["user_id"] = user.pk permission_info = user.roles.all().values( "permissions__url", # 權限url "permissions__code", # "permissions__title", "permissions__id", "permissions__permission_group_id", "permissions__parent", "permissions__parent_id", "permissions__permission_group__menu__caption", "permissions__permission_group__menu__id", ).distinct() print(permission_info) # 設置用戶權限 dic = {} for per_info in permission_info: gid = per_info["permissions__permission_group_id"] if gid not in dic: dic[gid] = { "urls": [per_info["permissions__url"]], "codes": [per_info["permissions__code"]] } else: dic[gid]["urls"].append(per_info["permissions__url"]) dic[gid]["codes"].append(per_info["permissions__code"]) request.session["permissions_dict"] = dic ## 設置用戶的菜單 permission_list = [] for permission_item in permission_info: temp = { "id":permission_item["permissions__id"], "title":permission_item["permissions__title"], "url":permission_item["permissions__url"], "pid":permission_item["permissions__parent_id"], "menu_name":permission_item["permissions__permission_group__menu__caption"], "menu_id":permission_item['permissions__permission_group__menu__id'], } permission_list.append(temp) request.session["permission_list"] = permission_list
因爲菜單是通用,每一個函數都是須要處理菜單的邏輯,全部單獨拿出來進行建立一個處理菜單的模塊,使用@register.inclusion_tag 標籤。
自定義register.inclusion_tag標籤
from django import template register=template.Library() @register.inclusion_tag("menu.html") def get_menu(request): permission_list = request.session["permission_list"] #############temp_dict:存儲全部放到菜單欄中的權限 temp_dict = {} print(permission_list) for item in permission_list: pid = item["pid"] if not pid: item["active"] = False temp_dict[item["id"]] = item #######將須要標中的active設置True # print(permission_list) current_path = request.path_info import re for item in permission_list: pid = item["pid"] url = "^%s$" % item["url"] if re.match(url, current_path): if pid: temp_dict[pid]["active"] = True else: item["active"] = True ########將temp_dict轉換爲最終的menu_dict的數據格式 menu_dict = {} for item in temp_dict.values(): if item["menu_id"] in menu_dict: temp = {"title": item["title"], "url": item["url"], "active": item["active"]}, menu_dict[item["menu_id"]]["children"].append(temp) if item["active"]: menu_dict[item["menu_id"]]["active"] = True else: menu_dict[item["menu_id"]] = { "title": item["menu_name"], "active": item["active"], "children": [ {"title": item["title"], "url": item["url"], "active": item["active"]}, ] } print(menu_dict) return {"menu_dict":menu_dict}
def m1(request): # # menu_dict = { # # 1: { # # "title": "菜單一", # # "active": False, # # "children": [ # # {"title": "添加用戶", "url": "xxxxxxxxxxx", "active": False}, # # {"title": "查看用戶", "url": "xxxxxxxxxxx", "active": False}, # # # # ]}, # # # # 2: { # # "title": "菜單二", # # "active": True, # # "children": [ # # {"title": "添加用戶", "url": "xxxxxxxxxxx", "active": True}, # # {"title": "查看用戶", "url": "xxxxxxxxxxx", "active": True}, # # # # ] # # # # }} # premission_list = request.session["permission_list"] print(premission_list) #存儲放到菜單欄中的權限 temp_dict = {} for item in premission_list: if not item["pid"]: item["active"] = False #添加到菜單欄時,添加一個是否展開的標誌 temp_dict[item["id"]]= item #將須要標中的active設置爲True current_path = request.path_info import re for item in premission_list: pid = item["pid"] url = "%s$"%item["url"] if re.match(url,current_path): if pid: #判斷是否是二級菜單,若是是,就會把該菜單上一級設置爲Ture temp_dict[pid]["active"]=True else: item["avtive"] = True #注意此時的item 和temp_dict 的數據同一條數據,這裏修改了True,temp_dict 也會改成True print(temp_dict) #將數據最終構形成最終的menu_dict數據 menu_dict = {} for item in temp_dict.values(): if item["menu_id"] in menu_dict: temp = {"title":item["title","url":item["url"],"active":item["avtive"]]} #定義一個本身的字典結構體 menu_dict[item["menu_id"]]["children"].append(temp) #把菜單添加到一級菜單中 if item["active"] == True: #若是二級菜單是展開的,那麼一級菜單也是展開的 menu_dict[item["menu_id"]]["active"] = True else: menu_dict[item["menu_id"]] = { "title":item["menu_name"], "active":False, "children":[ {"title":item["title"],"url":item["url"],"active":item["active"],} ] } print(menu_dict) return render(request, "m1.html")
前端頁面
{% load my_tags %} <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Title</title> <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css"> <style> .header { width: 100%; height: 50px; background-color: #336699; } .menu, .content { float: left; } .menu { width: 200px; height: 600px; background-color: darkgray; } .hide { display: none; } .menu .title { font-size: 16px; color: #336699 !important; margin: 20px 0; } .con a { margin-left: 30px; color: white; } .active { color: red !important; } </style> </head> <body> <div class="header"></div> <div class="box"> {% mul 1 2 %} {% get_menu request %} <div class="content"> {% block con %} {% endblock %} </div> </div> </body> </html>