Django之路- 如何開發通用且萬能的的權限框架組件

今天老男孩IT教育Python教學總監alex帶你開通Django之路python

需求討論數據庫

權限設計django

代碼設計架構

自定義權限鉤子app

 

業務場景分析


假設咱們在開發一個培訓機構的客戶關係管理系統系統分客戶管理、學員管理、教學管理3個大模塊每一個模塊大致功能以下框架

客戶管理
銷售人員能夠錄入客戶信息對客戶進行跟蹤爲客戶辦理報名手續
銷售人員能夠修改本身錄入的客戶信息
客戶信息不能刪除
銷售主管能夠查看銷售報表ide


學員管理 
學員能夠在線報名 
學員能夠查看本身的報名合同、學習有效期
學員能夠在線提交做業 、查看本身的成績函數

教學管理
管理員能夠建立新課程、班級
講師能夠建立上課紀錄
講師能夠在線點名、批做業post

從上面的需求中 咱們至少提取出了5個角色普通銷售、銷售主管、學員、講師、管理員 他們能作的事情都是不同的學習


如何設計一套權限組件來實現對上面各類不一樣功能進行有效的權限控制呢咱們確定不能LOW到爲每一個動做都一堆代碼來控制權限對吧 這些表面上看着各類不盡相同的功能,確定是能夠提取出一些相同的規律的仔細分析其實每一個功能本質上都是一個個的動做若是能把動做再抽象中具體權限條目而後把這些權限條目 再跟用戶關聯每一個用戶進行這個動做就檢查他沒有這個權限不就實現權限的控制了麼因爲這個系統是基於WEB的B/S架構咱們能夠把每一個動做的構成提取成如下的元素

一個動做 = 一條權限 = 一個url + 一種請求方法(get/post/put...) + 若干個請求參數

那咱們接下來須要作的就是把 一條條的權限條目定義出來而後跟用戶關聯上就能夠了


開發中須要的權限定義

什麼是權限

權限 就是對 軟件系統 中 各類資源 的 訪問和操做的控制

什麼時資源

在軟件系統中數據庫、內存、硬盤裏數據都是資源資源就是數據

 

動做

資源自己是靜態的 必須經過合適的動做對其進行訪問和操做咱們說要控制權限其實本質上是要對訪問 軟件中各類數據資源的動做進行控制 

動做又能夠分爲2種

資源操做動做訪問和操做各類數據資源好比訪問數據庫或文件裏的數據

業務邏輯事件動做訪問和操做的目的不是數據源自己而是藉助數據源而產生的一系列業務邏輯好比批量往遠程 主機上上傳一個文件你須要從數據庫中訪問主機列表但你真正要操做的是遠程的主機這個遠程的主機嚴格意義上來並非你的數據資源而是這個資源表明的實體。

 

權限受權

權限的使用者能夠是具體的我的、亦能夠是其它程序 這都不要緊咱們能夠把權限的受權主體統稱爲用戶 不管這個用戶後面是具體的人仍是一個程序對權限控制組件來說都不影響 。


權限必然是須要分組的把一組權限 分紅一個組受權給特定的一些用戶分出來的這個組就能夠稱爲角色。


權限 應該是能夠疊加的

 

權限組件的設計與代碼實現

咱們把權限組件的實現分3步權限條目的定義 權限條目與用戶的關聯權限組件與應用的結合

 

權限條目的定義

咱們前面講過如下概念 如今須要作的就是把咱們系統中全部的須要控制的權限 所對應的動做 提取成 一條條 url+請求方法+參數的集合就能夠如下是提取出來的幾條權限


一個動做 = 一條權限 = 一個url + 一種請求方法(get/post/put...) + 若干個請求參數


1
2
3
4
5
6
7
8
perm_dic = {
 
     'crm_table_index' :[ 'table_index' , 'GET' ,[],{},],   #能夠查看CRM APP裏全部數據庫表
     'crm_table_list' :[ 'table_list' , 'GET' ,[],{}],     #能夠查看每張表裏全部的數據
     'crm_table_list_view' :[ 'table_change' , 'GET' ,[],{}], #能夠訪問表裏每條數據的修改頁
     'crm_table_list_change' :[ 'table_change' , 'POST' ,[],{}],  #能夠對錶裏的每條數據進行修改
 
     }

  

字典裏的key是權限名 一會咱們須要用過這些權限名來跟用戶進行關聯

後面values列表裏第一個值如'table_index'是django中的url name在這裏必須相對的url name, 而不是絕對url路徑由於考慮到django url正則匹配的問題搞絕對路徑很差控制。 


values裏第2個值是http請求方法


values裏第3個[]是要求這個請求中必須帶有某些參數但不限定對數的值是什麼


values裏的第4個{}是要求這個請求中必須帶有某些參數而且限定所帶的參數必須等於特定的值

 

有的同窗看了上面的幾條權限定義後提出疑問說你這個權限的控制好像仍是粗粒度的 好比我想控制用戶只能訪問 客戶 表裏的 一條或多條特定的用戶怎麼辦

哈這個問題很好但很容易解決呀只須要在[] or {}裏指定參數就可呀好比要求http請求參數中必須包括指定的參數舉個例子 個人客戶表以下

 Customer表

裏面的status字段是用來區分客戶是否報名的 我如今的需求是只容許 用戶訪問客戶來源爲qq羣且 已報名的 客戶你怎麼控制

經過分析咱們得出這個動做的url爲

1
http: / / 127.0 . 0.1 : 9000 / kingadmin / crm / customer / ?source = qq&status = signed

客戶來源參數是source,報名狀態爲status那個人權限條目就能夠配置成

1
'crm_table_list' :[ 'table_list' , 'GET' ,[],{ 'source' : 'qq' 'status' : 'signed' }]

  

權限條目與用戶的關聯

咱們並無像其它權限系統同樣把權限定義的代碼寫到了數據裏了也許是由於我懶不想花時間去設計存放權限的表結構but anyway,基於現有的設計 咱們如何把權限條目與 用戶關聯起來呢

good news is 咱們能夠直接借用django自帶的權限系統 你們都知道 django admin 自帶了一個簡單的權限組件容許把用戶在使用admin過程當中控制到表級別的增刪改查程度但沒辦法對錶裏的某條數據控制權限即要麼容許訪問整張表要麼不容許訪問實現不了只容許用戶訪問表中的特定數據的控制。 

咱們雖然沒辦法對經過自帶的django admin 權限系統實現想要的權限控制可是能夠借用它的 權限 與用戶的關聯 邏輯自帶的權限系統容許用戶添加自定義權限條目方式以下 

1
2
3
4
5
6
7
8
class  Task(models.Model):
     ...
     class  Meta:
         permissions  =  (
             ( "view_task" "Can see available tasks" ),
             ( "change_task_status" "Can change the status of tasks" ),
             ( "close_task" "Can remove a task by setting its status as closed" ),
         )

這樣就添加了3條自定義權限的條目 而後 manage.py migrate 就能夠在django自帶的用戶表裏的permissions字段看到你剛添加的條目。

只要把剛添加的幾條權限移動的右邊的框裏那這個用戶就至關於有相應的權限了之後你在代碼裏經過如下語句就能夠斷定用戶是否有相應的權限。

1
user.has_perm( 'app.view_task' )

 

看到這有的同窗還在懵逼這個自帶的權限跟咱們剛纔本身定義的權限條目有半毛錢關係麼聰明的同窗已經看出來了 只要咱們把剛纔本身定義的perm_dic字典裏的全部key在這個META類的permissions元組裏。就至關於把用戶和它能夠操做的權限關聯起來了這就省掉了咱們必須本身寫權限與用戶關聯所須要的代碼了

 

權限組件與應用的結合

咱們但願咱們的權限組件是通用的可插拔的它必定要與具體的業務代碼分離之後能夠輕鬆把這個組件移植到其它的項目裏去所以這裏咱們採用裝飾器的模式把權限的檢查、控制封裝在一個裝飾器函數裏想對哪一個Views進行權限控制就只須要在這個views上加上裝飾器就能夠了。

1
2
3
@check_permission
def  table_change(request,app_name,table_name,obj_id):
     .....

 

那這個@check_permission裝飾器裏乾的事情就是如下幾步

  1. 拿到用戶請求的url+請求方法+參數到咱們的的perm_dic裏去一一匹配

  2. 當匹配到了對應的權限條目後就拿着這個條目所對應的權限名和當前的用戶 調用request.user.has_perm(權限名)

  3. 若是request.user.has_perm(權限名)返回爲True,就認爲該用戶有權限 直接放行不然則返回403頁面

   權限檢查代碼

 

加入自定義權限

仔細按上面的步驟走下來並玩了一會的同窗可能會發現一個問題這個組件對有些權限是控制不到的 就是涉及到一些業務邏輯的權限沒辦法控制  好比 我只容許 用戶訪問本身建立的客戶數據這個你怎麼控制 

經過控制用戶的請求參數是沒辦法實現的 由於你獲取到的request.user是個動態的值你必須經過代碼來判斷這條數據是不是由當前請求用戶建立的。 相似的業務邏輯還有不少你怎麼搞

仔細思考了10分鐘即然這裏必須涉及到必須容許開發人員經過自定義一些業務邏輯代碼來判斷用戶是否有權限的話那我在個人權限組件裏再提供一個權限自定義函數不就能夠了開發者能夠把自定的權限邏輯寫到函數裏個人權限組件 自動調用這個函數只要返回爲True就認爲有權限就能夠啦

  加入了自定義權限鉤子的代碼

權限配置條目

1
2
3
'crm_can_access_my_clients' :[ 'table_list' , 'GET' ,[],
                              { 'perm_check' : 33 , 'arg2' : 'test' },
                              custom_perm_logic.only_view_own_customers],

看最後面咱們加入的only_view_own_customers就是開發人員自已加的權限控制邏輯裏面想怎麼寫就怎麼寫

一、def only_view_own_customers(request,*args,**kwargs):

二、    print('perm test',request,args,kwargs)

三、

四、    consultant_id = request.GET.get('consultant')

五、    if consultant_id:

六、       consultant_id = int(consultant_id)

七、 

八、    print("consultant=1",type(consultant_id))

九、

十、    if consultant_id == request.user.id:

十一、       print("\033[31;1mchecking [%s]'s own customers, pass..\033[0m"% request.user)

十二、        return True

1三、    else:

1四、        print("\033[31;1muser can only view his's own customer...\033[0m")

1五、        return False


這樣萬通且通用的權限框架就開發完畢了權限的控制粒度可粗可細、可深可淺包君滿意之後要移植到其它django項目時 你惟一須要改的就是配置好perm_dic裏的權限條目

用完以爲好記得點贊噢  

更多精彩請關注老男孩IT教育python學院

相關文章
相關標籤/搜索