對外提供API不用django rest framework(DRF)就是旁門左道嗎?
基于Token的鑒權機制越來越多的用在了項目中,尤其是對于純後端隻對外提供API沒有web頁面的項目,例如我們通常所講的前後端分離架構中的純後端服務,隻提供API給前端,前端通過API提供的資料對頁面進行渲染展示或增加修改等,我們知道HTTP是一種無狀态的協定,也就是說後端服務并不知道是誰發來的請求,那麼如何校驗請求的合法性呢?這就需要通過一些方式對請求進行鑒權了
先來看看傳統的登入鑒權跟基于Token的鑒權有什麼差別
以Django的賬号密碼登入為例來說明傳統的驗證鑒權方式是怎麼工作的,當我們登入頁面輸入賬号密碼送出表單後,會發送請求給伺服器,伺服器對發送過來的賬号密碼進行驗證鑒權,驗證鑒權通過後,把使用者資訊記錄在伺服器端(django_session表中),同時傳回給浏覽器一個sessionid用來唯一辨別這個使用者,浏覽器将sessionid儲存在cookie中,之後浏覽器的每次請求都一并将sessionid發送給伺服器,伺服器根據sessionid與記錄的資訊做對比以驗證身份
Token的鑒權方式就清晰很多了,用戶端用自己的賬号密碼進行登入,服務端驗證鑒權,驗證鑒權通過生成Token傳回給用戶端,之後用戶端每次請求都将Token放在header裡一并發送,服務端收到請求時校驗Token以确定通路者身份
session的主要目的是給無狀态的HTTP協定添加狀态保持,通常在浏覽器作為用戶端的情況下比較通用。而Token的主要目的是為了鑒權,同時又不需要考慮CSRF防護以及跨域的問題,是以更多的用在專門給第三方提供API的情況下,用戶端請求無論是浏覽器發起還是其他的程式發起都能很好的支援。是以目前基于Token的鑒權機制幾乎已經成了前後端分離架構或者對外提供API通路的鑒權标準,得到廣泛使用
JSON Web Token(JWT)是目前Token鑒權機制下最流行的方案,網上關于JWT的介紹有很多,這裡不細說,隻講下Django如何利用JWT實作對API的認證鑒權,搜了幾乎所有的文章都是說JWT如何結合DRF使用的,如果你的項目沒有用到DRF架構,也不想僅僅為了鑒權API就引入龐大複雜的DRF架構,那麼可以接着往下看
我的需求如下:
1. 同一個view函數既給前端頁面提供資料,又對外提供API服務,要同時滿足基于賬号密碼的驗證和JWT驗證
2. 項目用了Django預設的權限系統,既能對賬号密碼登入的進行權限校驗,又能對基于JWT的請求進行權限校驗
PyJWT介紹
要實作上邊的需求1,我們首先得引入JWT子產品,python下有現成的PyJWT子產品可以直接用,先看下JWT的簡單用法
安裝PyJWT
利用PyJWT生成Token
>>> import jwt
這裡傳了三部分内容給JWT,
第一部分是一個Json對象,稱為Payload,主要用來存放有效的資訊,例如使用者名,過期時間等等所有你想要傳遞的資訊
第二部分是一個秘鑰字串,這個秘鑰主要用在下文Signature簽名中,服務端用來校驗Token合法性,這個秘鑰隻有服務端知道,不能洩露
第三部分指定了Signature簽名的算法
檢視生成的Token
>>> print(encoded_jwt)
JWT生成的Token是一個用兩個點(.)分割的長字元串
點分割成的三部分分别是Header頭部,Payload負載,Signature簽名:
Header.Payload.Signature
JWT是不加密的,任何人都可以讀的到其中的資訊,其中第一部分Header和第二部分Payload隻是對原始輸入的資訊轉成了base64編碼,第三部分Signature是用header+payload+secret_key進行加密的結果
可以直接用base64對Header和Payload進行解碼得到相應的資訊
>>> import base64
因為JWT不會對結果進行加密,是以不要儲存敏感資訊在Header或者Payload中,服務端也主要依靠最後的Signature來驗證Token是否有效以及有無被篡改
解密Token
>>> jwt.decode(encoded_jwt,
服務端在有秘鑰的情況下可以直接對JWT生成的Token進行解密,解密成功說明Token正确,且資料沒有被篡改
當然我們前文說了JWT并沒有對資料進行加密,如果沒有secret_key也可以直接擷取到Payload裡邊的資料,隻是缺少了簽名算法無法驗證資料是否準确,pyjwt也提供了直接擷取Payload資料的方法,如下
>>> jwt.decode(encoded_jwt, verify=False)
Django案例
Django要相容session認證的方式,還需要同時支援JWT,并且兩種驗證需要共用同一套權限系統,該如何處理呢?我們可以參考Django的解決方案:裝飾器,例如用來檢查使用者是否登入的
login_required
和用來檢查使用者是否有權限的
permission_required
兩個裝飾器,我們可以自己實作一個裝飾器,檢查使用者的認證模式,同時認證完成後驗證使用者是否有權限操作
于是一個
auth_permission_required
的裝飾器産生了:
from django.conf
在view使用時就可以用這個裝飾器來代替原本的
login_required
和
permission_required
裝飾器了
@auth_permission_required('account.select_user')
我們還需要一個生成使用者Token的方法,通過給User model添加一個token的靜态方法來處理
class User(AbstractBaseUser, PermissionsMixin):
可以直接通過使用者對象來生成Token:
>>> from accounts.models import User
生成的Token給到用戶端,用戶端就可以拿這個Token進行鑒權了
>>> import requests
這樣一個
auth_permission_required
方法就可以搞定上邊的全部需求了,簡單好用。

長按關注公衆号檢視更多原創文章
如果你覺得文章對你有幫助,請點右下角【好看】。如果你覺得讀的不盡興,推薦閱讀以下文章:
- Django+Echarts畫圖執行個體
- Django開發密碼管理表執行個體【附源碼】