本篇主要介紹 用戶狀態保持的兩種的方案 -- session 和 jwt_token,以及這兩種方案的實現方式,及優缺點對比。
引入:HTTP協議是一種無狀態的協議,而這就意味着如果用戶向我們的應用提供了用戶名和密碼進行用戶認證,那么下一次請求時,用戶還是要再一次進行用戶認證才行。
因為根據HTTP協議的無狀態性,在下一次請求時我們並不知道是哪個用戶發出的請求,所以為了讓我們能識別是哪個用戶發送的請求,則必須采取一些特殊的方式,比如 session、jwt_token等。
一、session
根據上面的應用,我可以通過session來保存用戶的登錄狀態,而Django默認提供了session存儲機制,且默認啟動了session。
session存儲方式:數據庫、本地緩存、混合存儲三種。通常采用本地緩存:
SESSION_ENGINE='django.contrib.sessions.backends.cache'
那么我們怎么使用用戶登錄狀態保持呢?
在用戶登錄或者注冊成功后,設置session。
class UserView(APIView): ...... # 實現用戶登錄或者注冊的業務邏輯 request.session['user_id'] = user.id request.session['is_login'] = True ..... # 返回響應
由於session是保存在服務器中,在設置session時會將 生成隨機字符串(sessionID),通過cookie發送給客戶端,而服務器端會同sessionID作為鍵並且將我們設置的進行加密計算形參長字符串作為值存儲在服務器端。
關於session存儲機制可參考:https://www.cnblogs.com/lenther2002/p/4822302.html
缺點:
- Session: 每個用戶經過我們的應用認證之后,我們的應用都要在服務端做一次記錄,以方便用戶下次請求的鑒別,通常而言session都是保存在內存中,而隨着認證用戶的增多,服務端的開銷會明顯增大。
- 擴展性: 用戶認證之后,服務端做認證記錄,如果認證的記錄被保存在內存中的話,這意味着用戶下次請求還必須要請求在這台服務器上,這樣才能拿到授權的資源,這樣在分布式的應用上,相應的限制了負載均衡器的能力。這也意味着限制了應用的擴展能力。
- CSRF: 因為是基於cookie來進行用戶識別的, cookie如果被截獲,用戶就會很容易受到跨站請求偽造的攻擊
二、jwt_token
基於token的鑒權機制類似於http協議也是無狀態的,它不需要在服務端去保留用戶的認證信息或者會話信息。這就意味着基於token認證機制的應用不需要去考慮用戶在哪一台服務器登錄了,這就為應用的擴展提供了便利。
其實現的基本流程如下:
- 用戶進行登錄或者注冊成功,服務器返回一個token
- 客戶端存儲token(可以存儲在cookie中,也可以存儲在本地硬盤)
- 每次請求均攜帶上這個token值
- 服務器校驗token值(Django擴展提供了jwt_token認證機制),返回數據
1、jwt是什么?
JWT是由三段信息構成的,將這三段信息文本用.
鏈接一起就構成了Jwt字符串。就像這樣:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
2、其構成如下:
1、header -- 頭部,主要用來聲明類型,這是jwt。並且聲明加密算法,通常為HMAC SHA256。
2、payload -- 載荷,存放標准中注冊的聲明(簽發方,接收方等)、添加用戶的相關信息等
3、signature -- 簽證,需要base64轉換后的header和base64裝換后的payload使用.
連接組成的字符串,然后通過header中聲明的加密方式進行利用secret_key進行
組合加密,而得到signature。
注:
- 在payload中不要存放敏感信息,因為這部分信息只是使用了base64進行了裝換,而不是加密。
- secret_key不要暴露,若暴露用戶可以根據該header和payload自行生產jwt。
3、它的實現機制如下:
即 用戶每次請求攜帶該token,Django進行認證時,會取出header 和payloads部分,並結合Django的秘鑰secret_key使用header中聲明的加密方式進行加密生成一個新的 signature2,對用戶攜帶的token中的第三部分 signature1進行對比,若用戶修改了token則生成的signature2與用戶攜帶的signature1不一致,則無法通過驗證。
1、是否有token 決定是否進行jwt認證。
2、token的正確性決定是否能夠通過jwt認證 。
優點:
- 因為json的通用性,所以JWT是可以進行跨語言支持的,像JAVA,JavaScript,NodeJS,PHP等很多語言都可以使用。
- 因為有了payload部分,所以JWT可以在自身存儲一些其他業務邏輯所必要的非敏感信息。
- 便於傳輸,jwt的構成非常簡單,字節占用很小,所以它是非常便於傳輸的。
- 它不需要在服務端保存會話信息, 所以它易於應用的擴展
實現:
1、安裝:
pip install djangorestframework-jwt
2、配置
REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework_jwt.authentication.JSONWebTokenAuthentication', 'rest_framework.authentication.SessionAuthentication', 'rest_framework.authentication.BasicAuthentication', ), } # JWT_EXPIRATION_DELTA 指明token的有效期 JWT_AUTH = { 'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1), }
3、實現
class UserView(APIView): ....... # 實現注冊或者登陸的業務邏輯 # 生成記錄登錄狀態的token jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER payload = jwt_payload_handler(user) token = jwt_encode_handler(payload) user.token = token ....... # 返回響應
注:前提:
這個token必須要在每次請求時傳遞給服務端,它應該保存在請求頭里, 另外,服務端要支持CORS(跨來源資源共享)
策略,一般我們在服務端這么做就可以了Access-Control-Allow-Origin: *
。
由於我們之前在上面 配置了默認的認證類包含了:
'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework_jwt.authentication.JSONWebTokenAuthentication', )
故用戶在每次請求時攜帶 token,Django的認證系統會自動對該token進行認證。若認證通過,則會用戶認證完成。
over~~~,簡單的介紹了兩次常用的用戶登錄狀態保持的方案~~~,關於session介紹的並不全,有機會再補充~~~