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

本節內容數據庫

需求討論django

權限設計架構

代碼設計app

自定義權限鉤子 框架

 

業務場景分析


本節課老男孩教育ALEX教你假設咱們在開發一個培訓機構的 客戶關係管理系統,系統分客戶管理、學員管理、教學管理3個大模塊,每一個模塊大致功能以下ide

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


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

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


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


如何設計一套權限組件來實現對上面各類不一樣功能進行有效的權限控制呢?咱們確定不能LOW到爲每一個動做都一堆代碼來控制權限對吧? 這些表面上看着各類不盡相同的功能,確定是能夠提取出一些相同的規律的,仔細分析,其實每一個功能本質上都是一個個的動做,若是能把動做再抽象中具體權限條目,而後把這些權限條目 再跟用戶關聯,每一個用戶進行這個動做,就檢查他沒有這個權限,不就實現權限的控制了麼?因爲這個系統是基於WEBB/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請求參數中必須包括指定的參數,舉個例子, 個人客戶表以下:

spacer.gif 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字段看到你剛添加的條目。

spacer.gif

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

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. 

3. 

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

4. 

5. 

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

6. 

spacer.gif 權限檢查代碼

 

加入自定義權限

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

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

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

 

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

權限配置條目

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就是開發人員自已加的權限控制邏輯,裏面想怎麼寫就怎麼寫

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

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

    else:

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

        return False

 

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

 

用完以爲好,記得點贊噢! 

 

官網網址:www.oldboyedu.com

相關文章
相關標籤/搜索