生活中到處有權限,好比,騰訊視頻開會員纔有觀看某個最新電影的權限,你有房間鑰匙就有了進入這個房間的權限,等等。一樣,程序開發過程當中也有權限,咱們今天說的權限指的是web網站權限,對於不一樣用戶訪問web服務時應該有不一樣的功能,如,一個公司有CEO、銷售主管、銷售等等,不一樣的用戶能訪問的服務也不是徹底相同的,因此這個時候就須要權限控制了。html
組件能夠減小重複開發,一次開發,在之後的項目中能夠一直使用,避免重複寫一樣的代碼。程序員
web程序是經過 url 的切換來查看不一樣的頁面(功能),因此權限指的其實就是一個含有正則表達式的URL,對url控制就是對權限的控制。web
結論:一我的有多少個權限就取決於他有多少個URL的訪問權限。正則表達式
根據實際狀況,通常一個用戶能夠有多個權限,一個權限也能夠分配給多個用戶,因此表結構能夠按照以下設計:數據庫
用戶表 權限表 id name id user_id url title alex 1 1 /customer/add 添加客戶 egon 2 1 /customers/list/ 查看客戶 1 /consult_records/add/ 添加跟進記錄 1 /consult_records/ 查看跟進記錄
分析:設計完以後思考一下這樣作有什麼問題?假設又新增了10個用戶,每一個用戶都有右表中的4個權限,那就要在權限表中新增40條記錄,這仍是隻有4個url的狀況,現實生活中一個web網站確定不止4個url,這意味着每增長一個用戶,你就要在權限表中增長若干條記錄,加劇了程序員的工做不說,這樣形成了數據庫空間的嚴重浪費,那麼思考一下如何改進!django
聰明的你必定有想法了,沒錯,若是對用戶進行劃分角色,而後對角色進行權限的分配,問題就解決了。session
分析:app
一我的能夠有多個角色函數
一個角色能夠有多我的網站
一個角色能夠有多個權限
一個權限能夠分配給多個角色
表結構設計:
用戶表:user user2role表 id name id user_id role_id alex 1 1 3 egon 2 2 3 角色表:role role2permission id title id role_id permission_id CEO 1 3 1 銷售總監 2 3 2 銷售 3 3 3 3 4 權限表:permission id url title /customer/add 添加客戶 /customers/list/ 查看客戶 /consult_records/add/ 添加跟進記錄 /consult_records/ 查看跟進記錄
通過調整以後,由原來的【基於用戶的權限控制】轉換成【基於角色的權限控制】,之後再進行分配權限時只須要給指定角色分配一次權限,給衆多用戶再次分配指定角色便可。
models.py中代碼以下:
from django.db import models # Create your models here. class User(models.Model): """ 用戶表 """ name = models.CharField(verbose_name='用戶名', max_length=32) password = models.CharField(verbose_name='密碼', max_length=32) roles = models.ManyToManyField(verbose_name='擁有的全部角色', to='Role') def __str__(self): return self.name class Role(models.Model): """ 角色表 """ title = models.CharField(verbose_name='角色名稱', max_length=32) permissions = models.ManyToManyField(verbose_name='擁有的全部權限', to='Permission') def __str__(self): return self.title class Permission(models.Model): """ 權限表 """ url = models.CharField(verbose_name='含正則的URL', max_length=32) title = models.CharField(verbose_name='標題', max_length=32) def __str__(self): return self.title
不經意間,咱們竟然設計出了一個經典的權限訪問控制系統:rbac(Role-Based Access Control)基於角色的權限訪問控制。
注意:如今的設計還不是最終版,但以後的設計都是在此版本基礎上擴增的,爲了可以更好的理解,咱們暫且在此基礎上繼續開發,直到遇到沒法知足的狀況,再進行整改。
一、首先按照上面model示例代碼建表,進行數據庫遷移,在admin.py中註冊表,而後再建立一個超級用戶,登陸admin錄入數據,以下:
二、修改admin的顯示格式,以permission表爲例:
在admin.py中,permission表的註冊方法admin.site.register(Permission)改成以下代碼:
# 自定義類,類名本身定,但必須繼承ModelAdmin class PermissionConfig(admin.ModelAdmin): list_display = ['pk', 'title', 'url'] ordering = ['pk'] # 按照主鍵從低到高 admin.site.register(Permission, PermissionConfig)
Permission表顯示效果以下,有沒有更直觀:
注意:使用django的admin添加數據時,必定要記得在admin.py中註冊你本身的表,不然登陸admin只能看見django自帶的表,看不見你本身在model中定義的表。
三、登陸視圖函數
def login(request): if request.method == 'POST': user = request.POST.get('user') pwd = request.POST.get('pwd') user_obj = User.objects.filter(name=user, password=pwd).first() if user_obj: request.session['user_id'] = user_obj.pk # 將用戶id注入session # 查詢當前登陸人的全部權限列表 ret = Role.objects.filter(user=user_obj).values('permissions__url').distinct() permission_list = [] for item in ret: permission_list.append(item['permissions__url']) print('權限列表', permission_list) # 將當前登陸人的權限列表注入session中 request.session['permission_list'] = permission_list return HttpResponse('登陸成功') return render(request, 'login.html')
四、控制權限的中間件
from django.utils.deprecation import MiddlewareMixin from django.shortcuts import redirect, HttpResponse import re class PermissionMiddleWare(MiddlewareMixin): def process_request(self, request): # 設置白名單放行 for reg in ["/login/", "/admin/*"]: ret = re.search(reg, request.path) if ret: return None # 檢驗是否登陸 user_id = request.session.get('user_id') if not user_id: return redirect('/login/') # 檢驗權限 permission_list = request.session.get('permission_list') for reg in permission_list: reg = '^%s$' % reg ret = re.search(reg, request.path) if ret: return None return HttpResponse('無權訪問')
django中其實有兩個配置文件,一個是咱們項目中能夠看見的settings.py文件,也就是用戶的配置文件,另外一個是django默認的配置文件global_settings.py,你有沒有注意到雖然有些參數咱們在本身的配置文件settings.py中雖然沒有定義,但其實他是有值的,沒錯,django是在默認的配置文件中爲咱們寫好了,同時容許咱們在本身的setting.py中從新定義覆蓋掉django默認值,由此得出用戶的配置文件優先級比django默認的高,所以,咱們知道了項目的配置參數由django和咱們本身定義的配置文件共同決定的,且django的默認配置文件包含咱們本身定義的配置文件,因此,當咱們導入配置文件時,應以以下方式:
from django.conf import settings