DRF之許可權認證頻率元件
在程式設計的世界中,我們認為,使用者輸入的所有資料都是不可靠的,不合法的,直接使用使用者輸入的資料是不安全的,不僅如此,我們還需要控制使用者的訪問行為,接下來,我們要學習認證元件、許可權元件、頻率元件。
引入
同學們,通過前面三節課的學習,我們已經詳細瞭解了DRF提供的幾個重要的工具,DRF充分利用了面向物件程式設計的思路,對Django的View類進行了繼承,並封裝了其as_view方法和dispatch方法,隨後提供了幾個非常方便的程式設計工具,比如解析器、序列化。
我們通過解析器,可以對來自客戶端的application/json資料進行解析,另外,通過序列化工具,我們能夠快速構建一套符合REST規範的api,隨後又通過DRF的mixin、view及viewset對這些介面邏輯進行優化。
有了他們,程式設計師開發Web應用的效率大大提高了,雖然我們也嘗試自己動手實現了這些功能,但是既然有了優秀的工具,我們就不必費盡心思重複發明輪子。DRF並不僅僅提供了這幾個工具,今天我們就來繼續深入學習DRF提供的一些其他的工具。
跟以往一樣,我們不僅僅要學會這些工具的使用方式,並且要深入研究他們的原始碼,希望大家在研究原始碼的過程中,能夠對面向物件程式設計的思路有更加深刻的認識。
當然,按照慣例,為了方便學習新知識以及閱讀原始碼,我們先來複習回顧一下之前已經學習過的知識。
今日概要
- retrieve方法原始碼剖析
- 認證元件的使用方式及原始碼剖析
- 許可權元件的使用方式及原始碼剖析
- 頻率元件的使用方式及原始碼剖析
知識點複習回顧
- 昨日回顧
- Python邏輯運算
知識點複習回顧一:Python邏輯運算
有了前兩天的基礎,今天看原始碼我們就沒有那麼大的壓力了,所要複習的知識也僅僅只有一個,那就是Python的邏輯運算,當然,稍後還會有幾個簡單的知識點,就不單獨拿出來複習了。
什麼是邏輯運算呢?就是and、or、not。not為取反,比較簡單,而and和or表示通過運算,計算表示式的布林值,判斷最終結果為真即止
- and:x and y 表示布林與,意為,判斷and運算之後的最終結果,為真即止,and運算必須表示式兩端所有值均為真才能確定最終結果,必須所有值都為真
- or:x and y 筆試布林或,意為,判斷or運算之後的最終結果,為真即止,or運算遇到真即返回,即有一個真值即可。
- not x:取反
看下面的程式碼吧:
1 |
x = 10 and 20 # x = 20 |
好了,知識點複習就這麼多。
今日詳細
mixin之retrieve原始碼剖析
上節課,我們分析過mixin中create方法的原始碼,今天,create方法比較簡單,今天,我們來分析分析retrieve方法的原始碼,它比create方法稍微複雜一點點,複雜的地方在於如何獲取需要操作的那條資料,因為我們知道,我們傳遞給不同的檢視類的所有方法都是一樣的,唯一變化的兩個是queryset和serializer_classes。
好了,廢話不多說,下面來分析一下:
- Django程式啟動,開始初始化,獲取配置資訊,獲取檢視類並載入到記憶體中,獲取url及檢視類的對應關係
- 開始繫結檢視類和url的對應關係,執行as_view()方法
- as_view()方法被執行的時候傳遞了引數,為字典形式:{ “get”: “retrieve”, “delete”: “destroy”, “put”: “update” }
- 上一步中執行as_view()方法傳遞引數的目的是為了完成優化,將delete請求方式重新命名為不同的函式
- ViewSetMixin類重寫了as_view()方法,也就是在這個地方將幾個函式重新繫結,它並沒有重寫dispatch方法
- 該方法返回檢視函式view,注意在這個函式中有一個行 self = cls(**initkwargs), cls是檢視類,執行檢視函式時self就指向檢視函式的例項物件
- 等待客戶端請求
- 請求到來,開始執行檢視函式,注意,呼叫檢視函式時的方式是view(request),而如果url帶有引數,呼叫方式為view(request, xxx=id)的形式
- 顯然,我們有命名引數(?P\d+),所以此時的呼叫方式為view(request, pk=id)
- 檢視函式中有一行self.kwargs = kwargs,所以pk已經被檢視函式找到了
- 檢視函式返回self.dispatch(),開始執行dispatch方法,注意self是檢視類的例項化物件(每個請求都被封裝為一個物件)
- dispatch開始執行get方法,注意此時的get方法會執行retrieve,以為已經被重定向了
- 開始執行retrieve,有一行instance = self.get_object(), 該方法在GenericAPIView中
- 至關重要的是拿到self.kwargs中的pk關鍵字,然後從queryset中拿到想要的資料
- 返回結果
從以上過程中我們可以看出,最關鍵的一步就是對kwargs的封裝,這就是玄機所在,看到這裡,你對面向物件有了什麼新的領悟嗎?對於反射呢,有了跟多的思考和理解嗎?
如果沒有,不用著急,任何質的飛躍都需要量的積累,等我們寫的多了,看得多了,自然就會突破瓶頸。
好了,同志們,這些內容算是對於檢視元件的進一步挖掘和吸收,至此,檢視元件我們就差不多講完了。接下來,我們要學習其他工具了。
認證元件
很久很久以前,Web站點只是作為瀏覽伺服器資源(資料)和其他資源的工具,甚少有什麼使用者互動之類的煩人的事情需要處理,所以,Web站點的開發這根本不關心什麼人在什麼時候訪問了什麼資源,不需要記錄任何資料,有客戶端請求,我即返回資料,簡單方便,每一個http請求都是新的,響應之後立即斷開連線。
而如今,網際網路的世界發生了翻天覆地的變化,使用者不僅僅需要跟其他使用者溝通交流,還需要跟伺服器互動,不管是論壇類、商城類、社交類、門戶類還是其他各類Web站點,大家都非常重視使用者互動,只有跟使用者互動了,才能進一步留住使用者,只有留住了使用者,才能知道使用者需求,知道了使用者需求,才會產生商機,有了使用者,就等於有了流量,才能夠騙到…額…抱歉…是融到錢,有了資金企業才能繼續發展,可見,使用者互動是非常重要的,甚至可以說是至關重要的一個基礎功能。
而談到使用者互動,則必須要談到我們今天所要學習的知識點,認證、許可權和頻率。首先我們來看看認證。
登入成功後生成token
之前我們學習過使用cookie和session兩種方式可以儲存使用者資訊,這兩種方式不同的是cookie儲存在客戶端瀏覽器中,而session儲存在伺服器中,他們各有優缺點,配合起來使用,可將重要的敏感的資訊儲存在session中,而在cookie中可以儲存不太敏感的資料。
今天我們要講到的是使用token的方式,token稱之為令牌。cookie、session和token都有其應用場景,沒有誰好誰壞,不過我們開發資料介面類的Web應用,目前用token還是比較多的。
token認證的大致步驟是這樣的:
- 使用者登入,伺服器端獲取使用者名稱密碼,查詢使用者表,如果存在該使用者且第一次登入(或者token過期),生成token,否則返回錯誤資訊
- 如果不是第一次登入,且token未過期,更新token值
接下來,我們建立兩個model,如下所示(token也可以儲存在user表中,不過建議儲存在user表中):
1 |
from django.db import models |
我們無需實現get方法,因為涉及登入認證,所有寫post方法介面,登入都是post請求,檢視類如下所示:
1 |
from django.http import JsonResponse |
簡單寫了個獲取隨機字串的方法用來生成token值:
1 |
# -*- coding: utf-8 -*- |
以上就是token的簡單生成方式,當然,在生產環境中不會如此簡單,關於token也有相關的庫,好了,我們構造幾條資料之後,可以通過POSTMAN工具來建立幾個使用者的token資訊。
接下來,如何對已經登入成功的使用者實現訪問授權呢?也就是說,只有登入過的使用者(有token值)才能訪問特定的資料,該DRF的認證元件出場了。
DRF認證元件使用
首先,我們來看一看,DRF認證元件的使用方式,首先,我們必須新建一個認證類,之後的認證邏輯就包含在這個類裡面:
1 |
class UserAuth(object): |
實現方式看上去非常簡單,到token表裡面檢視token是否存在,然後根據這個資訊,返回對應資訊即可,然後,在需要認證通過才能訪問的資料介面裡面註冊認證類即可:
1 |
class BookView(ModelViewSet): |
至於為什麼這麼寫,接下來,我們一起分析原始碼,大家就都非常清楚了。
DRF認證原始碼剖析
前面的步驟都差不多,我們來看有差別的地方,我們說,request物件是APIView重寫的,這個是在dispatch方法裡面實現的,繼續往後看dispatch方法,我們會看到self.initial方法,就是在這個方法裡面,我們會看到認證、許可權、頻率幾個元件的實現:
- 執行self.initial()方法
- 執行self.perform_authentication(request),方法,注意,新的request物件被傳遞進去了
- 該方法只有一行request.user,根據之前的經驗,解析器(request.data),我們知道這個user肯定也是request對的一個屬性方法
- 所料不錯,該方法繼續執行self._authenticate(),注意此時的self是request物件
- 該方法會迴圈self.authenticators,而這個變數是在重新例項化request物件時通過引數傳遞的
- 傳遞該引數是通過get_authenticatos()的返回值來確定的,它的返回值是
- [ auth for auth in self.authentication_classes ]
- 也就是我們的BookView裡面定義的那個類變數,也就是認證類
- 一切都明朗了,迴圈取到認證類,例項化,並且執行它的authenticate方法
- 這就是為什麼認證類裡面需要有該方法
- 如果沒有該方法,認證的邏輯就沒辦法執行
- 至於類裡面的header方法,照著寫就行,有興趣的可以研究原始碼,這裡就不細究了
- 該方法如果執行成功就返回一個元組,執行完畢
- 如果失敗,它會捕捉一個APIException
- 如果我們不希望認證通過,可以raise一個APIException
這就是認證元件的實現方式,非常簡單。
多個認證類的實現
並且,我們還可以指定多個認證類,只是需要注意的是,如果需要返回什麼資料,請在最後一個認證類中返回,因為如果在前面返回,在self._authentication()方法中會對返回值進行判斷,如果不為空,認證的過程就會中止,多個認證類的實現方式如下:
1 |
class UserAuth2(object): |
如果不希望每次都寫那個無用的authenticate_header方法,我們可以這樣:
1 |
from rest_framework.authentication import BaseAuthentication |
繼承BaseAuthentication類即可。
全域性認證
如果希望所有的資料介面都需要認證怎麼辦?很簡單,還是根據之前的經驗,就是這句程式碼:
1 |
authentication_classes=api_settings.DEFAULT_AUTHENTICATION_CLASSES |
如果認證類自己沒有authentication_classes,就會到settings中去找,通過這個機制,我們可以將認證類寫入到settings檔案中即可實現全域性認證:
1 |
REST_FRAMEWORK = { |
好了,認證到這裡就差不多了。接下來繼續介紹許可權元件
許可權元件
與認證元件幾乎差不多,我們直接看使用方式吧
許可權元件使用
定義許可權類:
1 |
class UserPerms(): |
同樣的邏輯,同樣的方式,只是執行許可權的方法名與執行認證的方法名不一樣而已,名為has_permission,並且需要將當前的檢視類傳遞給該方法。
檢視類中加入permission_classes變數:
1 |
class BookView(ModelViewSet): |
許可權元件原始碼剖析
許可權元件的原始碼與認證元件是一樣的。
頻率元件
使用自定義方式實現對ip地址進行訪問頻率控制
使用方式介紹,上面兩個元件也是幾乎一樣,只是用來做判斷的邏輯不一樣而已,下面是作業的答案:
throttles.py(該方式沒有DRF提供的方式簡潔)
1 |
import time |
檢視類中:
1 |
class BookView(ModelViewSet): |
使用DRF簡單頻率控制實現對使用者進行訪問頻率控制
區域性訪問頻率控制
1 |
from rest_framework.throttling import SimpleRateThrottle |
rate代表訪問評率,上面表示每分鐘五次,get_cache_key是必須存在的,它的返回值告訴當前頻率控制組件要使用什麼方式區分訪問者(比如ip地址)。
之後在檢視中使用即可:
1 |
from .utils.throttles import RateThrottle |
全域性訪問頻率控制
首先定義一個頻率控制類,並且必須繼承SimpleRateThrottle這個類,它是DRF提供的一個方便的頻率控制類,請看下面的程式碼:
1 |
class RateThrottle(SimpleRateThrottle): |
另外,我們需要在全域性配置頻率控制引數
1 |
REST_FRAMEWORK = { |
這樣就實現了,每分鐘最多五次訪問的邏輯。
今日總結
- retrieve方法原始碼剖析
- 認證元件的使用方式及原始碼剖析
- 許可權元件的使用方式及原始碼剖析
- 頻率元件的使用方式及原始碼剖析