Django權限管理之初步完整版
項目背景:這是一個權限管理系統(給一些角色和他們的權限指URL和頁面可以刪除的按鈕比如:增刪改查)
使用到了中間件,和初始化權限,使用了admin的后台管理系統。
我們這個是基於角色的權限訪問控制(Role-Based Access Control)做一個組件。
首先建立一個項目工程里面有另個應用:app01與rbac,
我們在rbac中model中建立一些數據類型代碼如下:
from django.db import models # Create your models her class Menu(models.Model):#定義一個菜單組 """菜單組""" title=models.CharField(max_length=32) class Group(models.Model):#權限組(用戶組與權限組) """權限組""" caption=models.CharField(verbose_name="組名稱",max_length=32)#組的名稱 menu=models.ForeignKey(verbose_name="所屬菜單",to="Menu",default=1)# class Permission(models.Model):#權限表 """權限表""" url=models.CharField(max_length=64,verbose_name="URL")#權限表的url title=models.CharField(max_length=64,verbose_name="標題")#權限表的名字 menu_gp=models.ForeignKey(verbose_name="組內菜單",to="Permission",null=True,blank=True)#自關聯 ,blank是在admin里可以設置為空,null是設置數據庫的字段可以為空。 code=models.CharField(verbose_name="代碼",max_length=16)#定義代碼 group=models.ForeignKey(verbose_name="所屬組",to="Group",null=True)#所屬組 class Meta:#在admin里面顯示表明 verbose_name_plural="權限表" def __str__(self):#打印名字 return self.title class Userinfo(models.Model):#用戶組 """用戶表""" username=models.CharField(verbose_name="名字",max_length=32)#用戶的名字 password=models.CharField(verbose_name="密碼",max_length=32)#用戶的password user_role=models.ManyToManyField(verbose_name="所有的角色",to="Role",blank=True)#和角色進行多對多關聯 class Meta:#在admin里面顯示表明 verbose_name_plural="用戶表" def __str__(self): return self.username class Role(models.Model):#角色名字 """角色表""" title=models.CharField(verbose_name="職位",max_length=32)#角色的名字 role_permission=models.ManyToManyField(verbose_name="權限",to="Permission",blank=True) class Meta: verbose_name_plural="角色表" def __str__(self): return self.title
再rbac中的admin中填入需要在生成的數據代碼如下:
from django.contrib import admin # Register your models here. from . import models#用djangoadmin時一定要在這里寫東西 admin.site.register(models.Permission) admin.site.register(models.Userinfo) admin.site.register(models.Role)
中間件的代碼如下:
import re from django.shortcuts import redirect,HttpResponse from django.conf import settings class MiddlewareMixin(object):#這個是中間件 def __init__(self, get_response=None): self.get_response = get_response super(MiddlewareMixin, self).__init__() def __call__(self, request): response = None if hasattr(self, 'process_request'): response = self.process_request(request) if not response: response = self.get_response(request) if hasattr(self, 'process_response'): response = self.process_response(request, response) return response class Middle(MiddlewareMixin): def process_request(self,request): #1 .獲取當前請求的URL # request.path_info #2. 獲取Session中保存當前用戶的權限 # request.session.get("permission_url_list") current_url=request.path_info#這個是獲取的當前請求的路徑 print("當前請求的路徑",current_url)#獲取當前的url for url in settings.VALID_URL:#這里設置白名單 if re.match(url,current_url):#匹配是否在白名單這個不要格式化 return None#返回None返回為空就可以經過下一個中間 permission_dict = request.session[settings.PERMISSION_URL_DICT_KEY]#獲取權限url和這個頁面的某些權限 print("有這些權限URl",permission_dict) if not permission_dict: return redirect("/login/")#如果沒有登錄就直接返回登錄頁面 flag = False#設置一個flag for group_id ,code_url in permission_dict.items():#字典循環獲取值用 字典名.items() for db_url in code_url["urls"]:#循環取里面的url regax="^{0}$".format(db_url)#對取出的url進行格式化 if re.match(regax,current_url):#通過匹配找出是否在這里面 request.permission_code_list=code_url["codes"] print("這里是獲取的是單個頁面里面的權限",request.permission_code_list) flag=True#如果匹配成功直接跳出去 break if flag: break if not flag:#如果沒有跳出說明他沒有權限 return HttpResponse("你沒有權限")
初始化權限的代碼:
from django.conf import settings def init__perimission(user,request):#初始化權限 perimission_list = user.user_role.values("role_permission__id",#權限的id "role_permission__title",#權限的名字 "role_permission__url",#權限的url # "role_permission__is_menu", "role_permission__code",#權限名字的代碼 "role_permission__menu_gp_id",#組內菜單的id(一個組的菜單id) "role_permission__group_id",#所屬的組 "role_permission__group__menu_id",#菜單id "role_permission__group__menu__title",#菜單的名字 ).distinct()#這里獲取的是的個人的權限與明細 print("這就是最終拿到的個人明細",perimission_list) # 更改菜單(這個菜單是在同一個組內的菜單不變始終存在) sub_perimission_list=[]#先定義一個列表 for item in perimission_list:#首先是循環里面所有的數據 tpl={ "id":item["role_permission__id"],#獲取id是權限的id "title":item["role_permission__title"],#獲取的是權限的名字 "url":item["role_permission__url"],#權限的url "menu_gp_id":item["role_permission__menu_gp_id"],#這是組內菜單的id "menu_id":item["role_permission__group__menu_id"],#這是菜單的id "menu_title":item["role_permission__group__menu__title"]#這是菜單的名字 } sub_perimission_list.append(tpl)#加入到列表中 request.session[settings.PERMISSION_MEN_KEY] = sub_perimission_list#復制到session "-----------以上是菜單的處理" #權限的處理 result ={}#先定義一個字典 for item in perimission_list:#從這個數據里循環數據 group_id=item["role_permission__group_id"]#所屬組 code=item["role_permission__code"]#一個頁面里的有哪些的權限 url=item["role_permission__url"]#權限對應的url if group_id in result: result[group_id]["codes"].append(code)#加入權限代碼 result[group_id]["urls"].append(url)#加入權限url else: result[group_id]={ "codes":[code,], "urls":[url,], } request.session[settings.PERMISSION_URL_DICT_KEY]=result#賦值權限
數據庫的持久化后接着用admin的后台填一些數據:
我們自己設置了關於網頁的自定義標簽:名字是rbac
import re from django.template import Library from django.conf import settings register = Library() #自定義的標簽 @register.inclusion_tag("xxxx.html")#從別的地方拿到數據 def menu_html(request): menu_list = request.session[settings.PERMISSION_MEN_KEY]#拿到菜單的數據 current_url = request.path_info#當前的url menu_dict = {}#定義一個字典 for item in menu_list:#先拿到所有的菜單 if not item["menu_gp_id"]:#判斷是否是菜單 menu_dict[item["id"]]=item#如果是就把數據賦值給他 for item in menu_list:#這個是循環所有的數據進行url的匹配 給要匹配的url匹配出要顯示哪個頁面 regex="^{0}$".format(item["url"])#先把數據進行格式化 if re.match(regex,current_url):#如果匹配到數據 menu_gp_id=item["menu_gp_id"]#最內菜單id if menu_gp_id:#如果是數字就給這個數字加上“active”=True menu_dict[menu_gp_id]["active"]=True else: menu_dict[item["id"]]["active"]=True#如果不是的當前的加上“active”=True result={}#先定義一個字典 for item in menu_dict.values():#這是字典的取值 active=item.get("active")#獲取它的active menu_id=item["menu_id"]#獲取菜單值 if menu_id in result: result[menu_id]["children"].append({"title":item["title"],"url":item["url"],"active":active}) if active: result[menu_id]["active"]=True#獲取菜單的active用來判斷是否加上hide else: result[menu_id]={ "menu_id":item["menu_id"], "menu_title":item["menu_title"], "active":active,#判斷 "children":[ { "titel":item["title"], "url":item["url"], "active":active #這個的話是用來判斷是否需要標紅的 } ] } return {'menu_dict':result}#返回用來顯示字典的
xxxx代碼:
{% for foo,item in menu_dict.items %}
<div class="item">
<div class="item-title">{{ item.menu_title }}</div>
{% if item.active %}
<div class="item-permission">
{% else %}
<div class="item-permission hide">
{% endif %}
{% for v in item.children %}
{% if v.active %}
<a href="{{ v.url }}" class="active">{{ v.titel }}</a>
{% else %}
<a href="{{ v.url }}">{{ v.titel }}</a>
{% endif %}
</div>
{% endfor %}
</div>
{% endfor %}
這是應用app01里調用代碼:名字是layout是模板
{% load rbac %}<!--這個是繼承自定義標簽的-->
<!--這個是模板以后都從這個繼承-->
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<link rel="stylesheet" href="/static/rbac/rbac.css"><!--這個是導入css樣式-->
</head>
<body>
<div class="header">
{% block header %}
<h1>歡迎回來</h1>
{% endblock header %}
</div>
<div class="siderbar">
{% block siderbar %}
{% menu_html request %}<!--這個是產生菜單的樣式-->
{% endblock siderbar %}
</div>
<div class="content">
{% block content %}
{% endblock content %}
</div>
<script src="/static/jquery-3.2.1.min.js"></script>
<script src="/static/rbac/rabc.js"></script><!--這個是導入js樣式-->
</body>
</html>
別的頁面也是繼承這個頁面:
{% extends 'layout.html' %} {% block content %} {% if page_permission.has_add %} <a href="/userinfo/add/">添加用戶11</a> {% endif %} <table> {% for user in userlist %} <tr> <td>{{ user.id }}</td> <td>{{ user.name }}</td> {% if page_permission.has_edit %} <td><a href="">編輯</a></td> {% endif %} {% if page_permission.has_del %} <td><a href="">刪除</a></td> {% endif %} </tr> {% endfor %} </table> {% endblock %}
在判斷一個權限是否屬於時可以建立一個類:
class BasePagePermission(object):#有某一個更改的權限 def __init__(self,code_list): self.code_list=code_list def has_add(self): if "add" in self.code_list: return True def has_edit(self): if "edit" in self.code_list: return True def has_del(self): if "del" in self.code_list: return True
項目app01中的view中的代碼:
import re from django.shortcuts import render,redirect,HttpResponse from rbac import models from rbac.server.init_permission import init__perimission def login(request): #登錄判斷 if request.method=="GET": return render(request,"login.html") else: name=request.POST.get("name") pwd=request.POST.get("pwd") user=models.Userinfo.objects.filter(username=name,password=pwd).first() print("用戶名",user) if not user: return render(request,"login.html") init__perimission(user,request)#初始化權限 return redirect("/index/")#返回權限 def index(request): return render(request,"index.html") class BasePagePermission(object):#有某一個更改的權限 def __init__(self,code_list): self.code_list=code_list def has_add(self): if "add" in self.code_list: return True def has_edit(self): if "edit" in self.code_list: return True def has_del(self): if "del" in self.code_list: return True def userinfo(request): pagepermission= BasePagePermission(request.permission_code_list)#查看有哪些表有哪些刪除或者修改的權限 data_list=[ { "id":1,"name":"xx1" }, { "id":2,"name":"xx1" }, { "id":3,"name":"xx1" }, { "id":4,"name":"xx1" }, { "id":5,"name":"xx1" } ] return render(request,"userinfo.html",{"userlist":data_list,"page_permission":pagepermission})#返回的是數據和是不是有這些權限的布爾值 def userinfoadd(request): userinfo_permission = BasePagePermission(request.permission_code_list) return render(request,"userinfo_add.html",{"gepermissionon":userinfo_permission}) class OrderPermission(BasePagePermission): def has_order(self): if "order" in self.code_list: return True def userinfodel(request,nid): return HttpResponse("刪除人員信息") def userinfoedit(request,nid): return HttpResponse("編輯人員信息") def orderinfo(request): order_permission = BasePagePermission(request.permission_code_list) return render(request,"orderinfo.html") def orderinfoadd(request): return HttpResponse("增加訂單") def orderinfodel(request,nid): return HttpResponse("刪除訂單") def orderinfoedit(request,nid): return HttpResponse("編輯訂單")
用到的js代碼:
$(function () { $('.item-title').click(function () { // if($(this).next().hasClass('hide')){ // $(this).next().removeClass('hide') // }else{ // $(this).next().addClass('hide') // } // alert(111) $(this).next().toggleClass('hide'); }) });
中間件的代碼結構:

在這次項目中我們要在settings里面配置白名單和兩個全局變量。

注:如果把組件導入到另一項目中,一定要在views中login函數中導入組件中的的models。源碼放在了78天准備練習中
