Django中已經為我們設置好了基本的權限系統,在定義好model同步數據庫后,在每個庫下面都會有一張 'auth_permission' 表。該表里面記錄了每條權限的描述(name字段,can do something,會顯示在后台付權限的列表中)和名字(codename字段,代碼邏輯中檢查權限使用,該字段會在權限驗證函數perm_check中被 'request.user.has_perm' 方法調用,來判斷是否通過驗證。)
權限系統的基本邏輯流程:
- 1、用戶發起操作請求時,根據url匹配到相應的試圖函數處理(返回相關信息)
- 2、在給用戶返回信息之前,首先驗證該用戶是否有權限查看所請求的信息(裝飾器)
- 3、裝飾器函數中,通過監測用戶http請求的url、method和參數來確定權限
- 4、若驗證通過,返回查詢信息(執行被裝飾器裝飾的函數)。若驗證不通過,返回 403 頁面
1、在每個給前端返回信息的views函數上添加裝飾器,
from permissions import check_permission #導入方法
@check_permission #向前段頁面返回客戶信息時,先執行裝飾器方法,驗證是否有權限 def customer(request): ,,,
@check_permission
def customer_info(request,customer_id):
,,,
2、model中添加規則(數據庫中添加權限紀錄)
class UserProfile(models.Model): user = models.OneToOneField(User) name = models.CharField(u"姓名",max_length=32) def __unicode__(self): return self.name class Meta: verbose_name_plural = u'用戶信息' permissions = ( #可以在任一表中添加 ('views_customer_list','顯示列表信息'), ('views_customer_info','顯示每條詳細信息'), ('edit_views_culstomer_info','修改詳細信息'), )
修改了models.py文件后,需要同步到數據庫。
sudo python manage.py makemigrations
sudo python manage.py migrate
3、定義權限匹配方法
appname/permission.py
#!_*_ coding:utf-8 _*_
from django.core.urlresolvers import resolve
from django.shortcuts import render,redirect
perm_dic ={
'views_customer_list':['customer_list','GET',[]], #請求到達時,先匹配url,然后匹配請求方法,最后匹配參數,若都通過,則付給views_customer_list的權限
'views_customer_info':['customer_info','GET',[]],
'edit_views_customer_info':['customer_info','POST',['test']] #這里['test']可以隨便填寫
}
#不確定函數參數時,用 *args,**kwargs
def perm_check(*args,**kwargs):
request = args[0]
#反向映射,將客戶端請求的url轉換為url別名,request.path_info為請求的完整url
url_resovle_obj = resolve(request.path_info)
#獲取url別名
current_url_namespace = url_resovle_obj.url_name
#設置標示符,要匹配(循環)好多層時,可以先設置一個標示符,這里默認不匹配
matched_flag = False
matched_perm_key = None
#如果存在url別名(url中必須有別名)
if current_url_namespace is not None:
#循環別名字典,取key值,即別名
for perm_key in perm_dic:
#字典里所有的值
perm_val = perm_dic[perm_key]
#格式必須為3個參數
if len(perm_val) == 3:
url_namespace,request_method,request_args = perm_val
#先判斷url是否匹配
if url_namespace == current_url_namespace:
#再判斷請求方法是否匹配
if request.method == request_method:
#如果別名字典(perm_dic)中參數[]為空,則直匹配前兩者即可
if not request_args:
#匹配成功
matched_flag = True
#將別名(字典key)付值給matched_perm_key
matched_perm_key = perm_key
#已匹配成功,退出循環
break
else: #字典中如果有參數,(參數可能有多個)
#循環所有的參數
for request_arg in request_args:
#反射獲取客戶端請求的方法,這樣就不用判斷是get還是post了
request_method_func = getattr(request,request_method)
#如果請求的每個參數都能匹配成功
if request_method_func.get(request_arg) is not None:
matched_flag = True #匹配成功
else:
matched_flag = False
break
#如果全都匹配成功
if matched_flag == True:
matched_perm_key = perm_key
break
else:
#如果沒有進行權限匹配,則默認通過
return True
#如果權限匹配全部通過
if matched_flag == True:
#stu_crm.views_customer_list
perm_str = "stu_crm.%s" %(matched_perm_key)
#.has_perm()方法django自帶,其參數為appname+auth_permission表中的codename字段,如果有此權限,則返回True
if request.user.has_perm(perm_str):
return True
else:
return False
else:
print("\033[41;1m ----- no matched permission ----\033[0m")
def check_permission(func):
def wrapper(*args,**kwargs):
#如果沒權限
if not perm_check(*args,**kwargs):
#args[0]即為request
return render(args[0],'stu_crm/403.html')
return func(*args,**kwargs)
return wrapper
4、修改customer.html,將固定的url用別名代替
<td><a href="{% url 'customer_info' customer.id %}">{{ customer.id }}</a></td>
5、修改url轉發規則,加入別名
appname/urls.py
urlpatterns = [ url(r'^$',views.dashboard), url(r'^customer/$',views.customer,name='customer_list'), url(r'^customer/(\d+)/$',views.customer_info,name='customer_info'), #必須添加url別名 ]
6、定義403頁面。
templates/403.html
設置完畢后,使用超級用戶登陸admin后台,在user中對每個普通用戶設置權限。每添加一條權限,在表 'auth_user_user_permissions' 中就會記錄一條。
注:應首先使用超級用戶將普通用戶的'Staff status'選項選中,以便首先允許其登陸。