用戶認證
1. cookie&session
- cookie:因為http請求是無狀態的,第一次和服務器連接后并且登錄成功后,返回一些數據(cookie)給瀏覽器,然后瀏覽器保存在本地,當該用戶發送第二次請求的時候,就會自動的把上次請求存儲的cookie數據自動的攜帶給服務器。
服務拿到cookie字符串就會進行解析為session={key:value}中的key,對應了找到這個用戶是否登錄is_login、用戶名username等一些信息。
-
設置cookie
新建一個應用來演示cookie相關的操作
-
views.py代碼
class Login(View): def get(self,request): # 設置cookie,cookie是在響應中返回的,所以要對響應對象去設置cookie response=HttpResponse() # set_cookie參數 # key:這個cookie的key,一般用JSessionID # value:這個cookie的value # max_age:最長的生命周期 # expires:過期時間 response.set_cookie("JSessionID","asdshgfkldsg999",max_age=120) response.content="請求成功" return response
-
使用Jmeter調用該接口
image 新建一個HTTP Cookie管理器
-
獲取cookie
-
views.py
def post(self,request): # 獲取cookie print(request.COOKIES) return HttpResponse("獲取cookie")
-
使用Jmeter調用該接口
image
-
-
存儲session
-
views.py
def put(self,request): # 存儲cookie cookie = request.COOKIES # session的key是用戶名,value是cookie的值 request.session["tim"] = cookie["JSessionID"] return HttpResponse("存儲cookie成功")
-
使用Jmeter調用該接口
image -
查看數據庫表Django_session,新增了session信息
image
-
2. 修改session的存儲機制
默認情況下,session數據是存儲到數據庫中的。當然也可以將session數據存儲到其他地方。可以通過設置SESSION_ENGINE
來更改session的存儲位置,這個可以配置為以下幾種方案:
- django.contrib.sessions.backends.db:使用數據庫。默認就是這種方案。
- django.contrib.sessions.backends.file:使用文件來存儲session。
- django.contrib.sessions.backends.cache:使用緩存來存儲session。想要將數據存儲到緩存中,前提是你必須要在settings.py中配置好CACHES,并且是需要使用Memcached,而不能使用純內存作為緩存。
- django.contrib.sessions.backends.cached_db:在存儲數據的時候,會將數據先存到緩存中,再存到數據庫中。這樣就可以保證萬一緩存系統出現問題,session數據也不會丟失。在獲取數據的時候,會先從緩存中獲取,如果緩存中沒有,那么就會從數據庫中獲取。
- django.contrib.sessions.backends.signed_cookies:將session信息加密后存儲到瀏覽器的cookie中。這種方式要注意安全,建議設置SESSION_COOKIE_HTTPONLY=True,那么在瀏覽器中不能通過js來操作session數據,并且還需要對settings.py中的SECRET_KEY進行保密,因為一旦別人知道這個SECRET_KEY,那么就可以進行解密。另外還有就是在cookie中,存儲的數據不能超過4k。
3. 項目應用
使用django自帶的認證、登錄、退出賬號的方法,記得自己配置下urls.py,不再展示
-
先創建django自帶鑒權相關表
python manage.py makemigrations python manage.py migrate
數據庫會生成對應的用戶權限相關表和session的表
image -
禁掉CSRF中間件
在django處理請求的過程中,需要經過中間件的過濾,涉及到跨站請求偽造時,django會把請求阻止過濾掉,所以我們要在setting.py中禁用跨站請求偽造的中間件,如果不禁用,好像會報一個403的錯誤。
image -
配置settings.py
SESSION_ENGINE = 'django.contrib.sessions.backends.db' # 配置session引擎,表示你想把session存到什么地方(session可以存在數據庫、緩存、文件里) SESSION_COOKIE_NAME = 'sessionid' # Session的cookie保存在瀏覽器上時的key,即:sessionid=隨機字符串 SESSION_COOKIE_PATH = "/" # Session的cookie保存的路徑(默認) SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名 SESSION_COOKIE_SECURE = False # 是否Https傳輸cookie SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http傳輸 SESSION_COOKIE_AGE = 60*30 # Session的cookie失效日期(30分鐘),不要寫成1800,這樣不易讀,比如表示8小時,寫為60*60*8 SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否關閉瀏覽器使得Session過期 SESSION_SAVE_EVERY_REQUEST = False # 是否每次請求都保存Session,默認修改之后才保存
image -
認證、登錄、退出賬號的代碼
from django.contrib.auth import authenticate, login, logout from django.contrib.auth.decorators import login_required from django.contrib.auth.models import User from django.http import HttpResponse, JsonResponse import json # 使用django自帶的認證、登錄、登出方法 def signup(request): '''用戶注冊方法''' data = json.loads(request.body) # django的創建用戶的方法,request傳入username、password、email是必填的 user = User.objects.create_user(username=data['username'], password=data['password'], email=data['email']) # 使用User模塊的create_user方法創建用戶 user.save() # 保存 # 返回一個響應信息 info = { "code": '0000', 'msg': "用戶注冊成功", 'data': { "id": user.id, "username": user.username, "password": user.password } } return JsonResponse(info) # 使用JsonResponse返回一個json字符串 def user_login(request): ''' 用戶登錄方法 :param request: :return: ''' if request.method == 'GET': return HttpResponse("腦補一個登錄頁面") data = json.loads(request.body) user = authenticate(username=data["username"], password=data["password"]) # 返回一個user對象 info = { "code": None, 'msg': None } if user is not None: if user.is_active: # is_active字段表示此條用戶名和信息是否有效 login(request, user) # 調用django.contrib.auth模塊的login info["code"] = "0000" info["msg"] = "登錄成功" else: info["code"] = "9999" info["msg"] = "該賬戶不可用" else: info["code"] = "9999" info["msg"] = "用戶名密碼不正確" return JsonResponse(info) def user_logout(request): '''退出登錄''' logout(request) # 調用django.contrib.auth模塊的logout方法 info = { "code": "0000", 'msg': "退出登錄成功" } return JsonResponse(info) def test(request): return HttpResponse("腦補一個登錄頁面") @login_required(login_url='/login_demo/test') # 此裝飾器會校驗會在訪問該視圖之前,校驗session,若通過則繼續,不通過則重定向到login_url指定的視圖中去 def test_user(request): ''' 測試登錄 :param request: :return: ''' return HttpResponse('ok')
-
使用Jmeter調用注冊接口
image
image -
數據庫auth_user表新增了一條用戶信息
image可見,用戶密碼是加密的。需要通過django的authenticate方法來驗證用戶名、密碼是否正確。
-
使用Jmeter調用登錄接口
imageimage接口響應頭返回一個sessionid:
image數據庫表django_session中,新增一條記錄,可以看出返回的sessionid就是這條記錄的session_key
image -
哪些視圖需要用戶認證呢?
使用裝飾器
@login_required(login_url='/login_demo/test')
,此裝飾器會校驗會在訪問該視圖之前,校驗session,若通過則繼續,不通過則重定向到login_url指定的視圖中去session過期后,調用testuser,重定向到test
imageimage登錄之后,調用testuser
imageps: 在每個視圖上加一個裝飾器其實很麻煩,此處只是了解原理,后面學習drf框架時,會使用新的方式進行用戶認證。
-
- 擴展內容
- 用戶組管理
- 權限管理
緩存
(暫時不深入講,會在drf部分講我們開發所需要的一點知識)
中間件
1. 簡介
在Django中,中間件(middleware)其實就是一個類,在請求到來和結束后,Django會根據自己的規則在合適的時機執行中間件中相應的方法。
- 1.執行完所有的request方法到達執行流程;
- 2.執行中間件的其他方法;
- 3.經過所有response方法,返回客戶端;
在一個項目中,如果想對全局所有視圖函數或視圖類起作用時,就可以在中間件中實現,比如想實現用戶登錄判斷,基于用戶的權限管理(RBAC)等都可以在Django中間件中來進行操作。
在settings.py文件中,注冊該中間件(Django項目中的settings模塊中,有一個MIDDLEWARE_CLASSES變量,其中每個元素都是一個中間件)
MIDDLEWARE_CLASSES = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
-
引子
中間件是 Django用來處理請求和響應的鉤子框架。它是一個輕量級的、底層級的“插件”系統,用于全局性地控制Django 的輸入或輸出,可以理解為內置的app或者小框架。
在django.core.handlers.base模塊中定義了如何接入中間件,這也是學習Django源碼的入口之一。
每個中間件組件負責實現一些特定的功能。例如,Django 包含一個中間件組件 AuthenticationMiddleware,它使用會話機制將用戶與請求request關聯起來。
中間件可以放在你的工程的任何地方,并以Python路徑的方式進行訪問。
Django 具有一些內置的中間件,并自動開啟了其中的一部分,我們可以根據自己的需要進行調整。
-
如何啟用中間件
若要啟用中間件組件,請將其添加到 Django 配置文件settings.py的 MIDDLEWARE 配置項列表中。
在 MIDDLEWARE 中,中間件由字符串表示。這個字符串以圓點分隔,指向中間件工廠的類或函數名的完整 Python 路徑。下面是使用 django-admin startproject命令創建工程后,默認的中間件配置:
MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ]
實際上在Django中可以不使用任何中間件,如果你愿意的話,MIDDLEWARE 配置項可以為空。但是強烈建議至少使用 CommonMiddleware。建議是保持默認的配置,這有助于你提高網站的安全性。
-
中間件最關鍵的順序問題
MIDDLEWARE
的順序很重要,具有先后關系,因為有些中間件會依賴其他中間件。例如:AuthenticationMiddleware
需要在會話中間件中存儲的經過身份驗證的用戶信息,因此它必須在SessionMiddleware
后面運行 。在請求階段,調用視圖之前,Django 按照定義的順序執行中間件 MIDDLEWARE,自頂向下。
如果某個層的執行過程認為當前的請求應該被拒絕,或者發生了某些錯誤,導致短路,直接返回了一個響應,那么剩下的中間件以及核心的視圖函數都不會被執行。
-
Django內置的中間件
Django內置了下面這些中間件,滿足了我們一般的需求:
-
Cache
緩存中間件
如果啟用了該中間件,Django會以CACHE_MIDDLEWARE_SECONDS 配置的參數進行全站級別的緩存。
-
Common
通用中間件
-
2. 中間件方法
傳統方式自定義中間件其實就是在編寫五大鉤子函數:
process_request(self,request)
process_response(self, request, response)
process_view(self, request, view_func, view_args, view_kwargs)
process_exception(self, request, exception)
process_template_response(self,request,response)
可以實現其中的任意一個或多個
3. 自定義中間件
有時候,為了實現一些特定的需求,我們可能需要編寫自己的中間件。
在編寫方式上,需要注意的是,當前Django版本2.2,存在兩種編寫的方式。一種是Django當前官網上提供的例子,一種是老版本的方式。本質上,兩種方式其實是一樣的。
我們先看一下傳統的,也是技術文章最多,目前使用最多的方式。
Django 提供的 get_response 方法可能是一個實際視圖(如果當前中間件是最后列出的中間件),或者是列表中的下一個中間件。我們不需要知道或關心它到底是什么,它只是代表了下一步要進行的操作。
兩個注意事項:
- Django僅使用 get_response 參數初始化中間件,因此不能為 init() 添加其他參數。
- 與每次請求都會調用 call() 方法不同,當 Web 服務器啟動后,init() 只被調用一次。
-
新建一個應用來演示中間件
python manage.py startapp middle_demo
(自行配置urls.py)
-
應用下新建一個middleware.py
class SimpleMiddleware1: # 第一個中間件示例 # 固定寫法, 必須有 def __init__(self, get_response): self.get_response = get_response # 配置和初始化 # call方法中定義一些中間件的操作 def __call__(self, request): # 在這里編寫視圖和后面的中間件被調用之前需要執行的代碼 # 這里其實就是舊的process_request()方法的代碼 print("SimpleMiddleware1的process_request()方法被調用") response = self.get_response(request) # 在這里編寫視圖調用后需要執行的代碼 # 這里其實就是舊的process_response()方法的代碼 print("SimpleMiddleware1的process_response()方法被調用") return response def process_view(self, request, view_func, view_args, view_kwargs): print("SimpleMiddleware1的process_view()方法被調用") # 出現異常時,會執行這個代碼 def process_exception(self,request,exception): print("SimpleMiddleware1的process_exception()方法被調用") def process_template_response(self,request,response): # 默認不執行這個函數,除非views函數中返回的實例對象(注意這里這個詞)中有render()方法 print("SimpleMiddleware1的process_template_response()方法被調用") return response class SimpleMiddleware2: # 第二個中間件示例 # 固定寫法, 必須有 def __init__(self, get_response): self.get_response = get_response # 配置和初始化 # call方法中定義一些中間件的操作 def __call__(self, request): # 在這里編寫視圖和后面的中間件被調用之前需要執行的代碼 # 這里其實就是舊的process_request()方法的代碼 print("SimpleMiddleware2的process_request()方法被調用") response = self.get_response(request) # 在這里編寫視圖調用后需要執行的代碼 # 這里其實就是舊的process_response()方法的代碼 print("SimpleMiddleware2的process_response()方法被調用") return response def process_view(self, request, view_func, view_args, view_kwargs): print("SimpleMiddleware2的process_view()方法被調用") # 出現異常時,會執行這個代碼 def process_exception(self,request,exception): print("SimpleMiddleware2的process_exception()方法被調用") def process_template_response(self,request,response): # 默認不執行這個函數,除非views函數中返回的實例對象(注意這里這個詞)中有render()方法 print("SimpleMiddleware2的process_template_response()方法被調用") return response
-
配置settings.py
MIDDLEWARE = [ 'middle_demo.middleware.SimpleMiddleware1', 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', # 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'middle_demo.middleware.SimpleMiddleware2', ]
可以調用我們之前配置好的登錄接口,在控制臺上看到中間件的順序:
image
4. 中間件應用
-
應用實例一:IP攔截
如果我們想限制某些IP對服務器的訪問,可以在settings.py中添加一個BLACKLIST(全大寫)列表,將被限制的IP地址寫入其中。然后,我們就可以編寫下面的中間件了:
from django.http import HttpResponseForbidden from django.conf import settings class BlackListMiddleware(): def __init__(self, get_response): self.get_response = get_response def __call__(self, request): if request.META['REMOTE_ADDR'] in getattr(settings, "BLACKLIST", []): return HttpResponseForbidden('<h1>該IP地址被限制訪問!</h1>') response = self.get_response(request) return response
-
應用實例二:DEBUG頁面
網站上線正式運行后,我們會將DEBUG改為 False,這樣更安全。但是發生服務器5xx系列錯誤時,管理員卻不能看到錯誤詳情,調試很不方便。有沒有辦法比較方便地解決這個問題呢?
- 普通訪問者看到的是500錯誤頁面
- 管理員看到的是錯誤詳情Debug頁面
利用中間件就可以做到!代碼如下:
import sys from django.views.debug import technical_500_response from django.conf import settings class DebugMiddleware(): def __init__(self, get_response): self.get_response = get_response def __call__(self, request): response = self.get_response(request) return response def process_exception(self, request, exception): # 如果是管理員,則返回一個特殊的響應對象,也就是Debug頁面 # 如果是普通用戶,則返回None,交給默認的流程處理 if request.user.is_superuser or request.META.get('REMOTE_ADDR') in settings.ADMIN_IP: return technical_500_response(request, *sys.exc_info())
這里通過if判斷,當前登錄的用戶是否超級管理員,或者當前用戶的IP地址是否在管理員IP地址列表中。符合兩者之一,即判斷當前用戶有權限查看Debug頁面。
接下來注冊中間件,然后在測試視圖中添加一行raise。再修改settings.py,將Debug設為False,提供ALLOWED_HOSTS = ["*"],設置比如ADMIN_IP = ['192.168.0.100'],然后啟動服務器0.0.0.0:8000,從不同的局域網IP來測試這個中間件。
正常情況下,管理員應該看到類似下面的Debug頁面:
RuntimeError at /midtest/ No active exception to reraise Request Method: GET Request URL: http://192.168.0.100:8000/midtest/ Django Version: 2.0.7 Exception Type: RuntimeError Exception Value: No active exception to reraise .....
而普通用戶只能看到:
A server error occurred. Please contact the administrator.
安裝redis
安裝參考:http://www.lxweimin.com/p/bb7c19c5fc47
pycharm集成化插件Iedis
安裝參考:https://blog.csdn.net/babados/article/details/78575145
因在mac上是付費的(不知道win要不要付費),故沒有使用。
自己使用了叫做Redis桌面管理工具的軟件:http://www.pc6.com/mac/486661.html
redis配置和測試
-
安裝django-redis
pip install django-redis==4.11.0
-
配置settings.py
添加:
CACHES = { # 配置緩存數據為redis "default": { "BACKEND": "django_redis.cache.RedisCache", "LOCATION": "redis://127.0.0.1:6379", "OPTIONS": { "CLIENT_CLASS": "django_redis.client.DefaultClient", "PASSWORD":"12345" #redis密碼,如果沒設密碼可以不用配置 } } }
-
測試redis安裝是否成功
# 終端輸入 # 啟動redis 服務端 redis-server # 啟動redis客戶端 redis-cli
from django_redis import get_redis_connection
image -
獲取緩存庫鏈接
cache = get_redis_connection("default") # default是settings.py中配置的項
image
-
添加緩存數據
cache.set("abc","aaaa1234",30*60)
image在redis客戶端可以查看
image
token
-
token簡介
在實現登錄功能的時候,正常的B/S應用都會使用cookie+session的方式來做身份驗證,后臺直接向cookie中寫數據,但是由于移動端的存在,移動端是沒有cookie機制的,所以使用token可以實現移動端和客戶端的token通信。
-
驗證流程
整個基于Token的驗證流程如下:
- 客戶端使用用戶名跟密碼請求登錄
- 服務器收到請求,去驗證用戶名和密碼
- 驗證成功后,服務端會簽發一個Token,再把這個Token發送到客戶端
客戶端收到的Token以后可以把它存儲起來,比如放在Cookie或LocalStorage里 - 客戶端每次向服務器發送其他請求的時候都要帶著服務器簽發的Token
- 服務器收到請求,去驗證客戶端請求里面帶著的Token,如果驗證成功,就像客戶端返回請求的數據
-
-
JWT標準
構造Token的方法挺多的,可以說只要是客戶端和服務器端約定好了格式,是想怎么寫就怎么寫的,然而還有一些標準寫法,例如JWT讀作/jot/,表示:JSON Web Tokens。
JWT標準的Token有三個部分:
- header
- payload
- signature
三個部分會用點分割開,并且都會使用Base64編碼,所以真正的Token看起來像這樣:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJuaW5naGFvLm5ldCIsImV4cCI6IjE0Mzg5NTU0NDUiLCJuYW1lIjoid2FuZ2hhbyIsImFkbWluIjp0cnVlfQ.SwyHTEx_RQppr97g4J5lKXtabJecpejuef8AqKYMAJc
-
Header
header部分主要是兩部分內容,一個是Token的類型,另一個是使用的算法,比如下面的類型就是JWT,使用的算法是HS256:
{ "typ": "JWT", "alg": "HS256" }
上面的內容要用 Base64 的形式編碼一下,所以就變成這樣:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
-
Payload
Payload 里面是 Token 的具體內容,這些內容里面有一些是標準字段,你也可以添加其它需要的內容。下面是標準字段:- iss:Issuer,發行者
- sub:Subject,主題
- aud:Audience,觀眾
- exp:Expiration time,過期時間
- nbf:Not before
- iat:Issued at,發行時間
- jti:JWT ID
-
Signature
JWT的最后一部分是Signature,這部分相當于前兩段的摘要,用來防止其他人來篡改Token中的信息,在處理時可以首先將前兩段生成的內容使用Base64生成一下再加鹽然后利用MD5等摘要算法在生成一遍。
-
token生成
-
服務端生成Token
在服務端生成Token的時候,需要解決兩個問題
- 使用什么加密算法
- Token如何存儲
-
加密算法
這里的加密算法并不是MD5,SHA1這樣的哈希算法,因為這種算法是無法解密的,只能用來生成摘要,在Django中內置了一個加密前面模塊django.core.signing模塊,可以用來加密和解密任何數據,使用簽名模塊的dumps和load函數來實現。
from django.core import signing value = signing.dumps({"foo":"bar"}) src = signing.loads(value) print(value) print(src)
結果是
eyJmb28iOiJiYXIifQ:1NMg1b:zGcDE4-TCkaeGzLeW9UQwZesciI {‘foo’: ‘bar’}
-
-
token存儲
項目文件下新建一個utils目錄,token.py
import time from django.core import signing import hashlib from django_redis import get_redis_connection cache = get_redis_connection("default") HEADER = {'typ': 'JWT', 'alg': 'default'} KEY = 'DAYBREAK' SALT = 'www.daybreak.com' # 生成摘要的鹽 TIME_OUT = 30 * 60 # 30min def encrypt(obj): """加密""" value = signing.dumps(obj, key=KEY, salt=SALT) value = signing.b64_encode(value.encode()).decode() return value def decrypt(src): """解密""" src = signing.b64_decode(src.encode()).decode() raw = signing.loads(src, key=KEY, salt=SALT) return raw # 重點掌握 def create_token(username): """生成token信息""" # 1. 加密頭信息 header = encrypt(HEADER) # 2. 構造Payload payload = {"username": username, "iat": time.time()} payload = encrypt(payload) # 3. 生成簽名 md5 = hashlib.md5() md5.update(("%s.%s" % (header, payload)).encode()) signature = md5.hexdigest() token = "%s.%s.%s" % (header, payload, signature) # 存儲到緩存中 cache.set(username, token, TIME_OUT) return token def get_payload(token): payload = str(token).split('.')[1] payload = decrypt(payload) return payload # 通過token獲取用戶名 def get_username(token): payload = get_payload(token) return payload['username'] # 重點掌握 def check_token(token): if token is None: return False username = get_username(token) last_token = cache.get(username) if last_token: cache.expire(username, TIME_OUT) return True return False def delete_token(username): last_token = cache.get(username) if last_token: cache.delete(username) return True return False
-
重點理解
create_token()
創建token、check_token()
驗證tokenfrom utils.token import * token = create_token("leitx") # 生成token check_token(token) # 驗證token,認證成功返回True
image
-
WSGIHTTP
基于中間件實現用戶登錄驗證
-
utils目錄下,新建login_middleware.py
請求之前就要做處理,所以不需要實現后面這些方法
process_view
、process_exception
、process_template_response
import json import re from utils.token import check_token # 設置白名單,這些請求不需要驗證token,比如注冊、登錄接口 from django.http import HttpResponse white_list = ['/middle_demo/login/', '/middle_demo/signup/'] # 設置黑名單,作為示例 black_list = ['/middle_demo/black/'] class LoginMiddleware: # 固定寫法, 必須有 def __init__(self, get_response): self.get_response = get_response # 配置和初始化 # call方法中定義一些中間件的操作 def __call__(self, request): request_url = request.path_info # 獲取請求的url # 通過正則判斷url # for p in white_list: # r = re.compile(p) # if r.match(request_url): # response = self.get_response(request) # return response # 不會正則的這樣判斷 # 如果是白名單 for p in white_list: if request_url in p: response = self.get_response(request) return response # 如果是黑名單 for p in black_list: if request_url in p: response = HttpResponse() response.content = json.dumps({"code": "9999", "message": "非法請求", "data": None}) response["Content-Type"] = "application/json;charset=UTF-8" return response # 獲取請求頭token的值 token = request.META.get("HTTP_TOKEN") if check_token(token): response = self.get_response(request) return response response = HttpResponse() response.content = json.dumps({"code": "9999", "message": "用戶未登錄或token過期", "data": None}) response["Content-Type"] = "application/json;charset=UTF-8" return response
-
request.META
是一個Python字典包含了所有本次HTTP請求的Header信息,他把請求頭的中的key都轉為大寫,并都加上HTTP_ 前綴,所以獲取請求token值時,key使用"HTTP_TOKEN"
-
-
配置settings.py中的中間件
因為中間件代碼中有return,觸發return時,不會再執行后面的中間件request部分,所以一般自定義的中間件都放在最下面。
MIDDLEWARE = [ 'middle_demo.middleware.SimpleMiddleware1', 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', # 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'middle_demo.middleware.SimpleMiddleware2', 'utils.login_middleware.LoginMiddleware', ]
-
在middle_demo的app中的view.py
import json from django.contrib.auth import authenticate from django.contrib.auth.models import User from django.http import JsonResponse, HttpResponse from django.views import View from utils.token import create_token # Create your views here. def bye(request): print("想說拜拜") return HttpResponse("成功獲取bye") class Signup(View): def post(self, request): data = json.loads(request.body) username = data.get("username",None) password = data.get("password",None) email = data.get("email",None) try: user = User.objects.create_user(username=username, password=password, email=email) token = create_token(user.username) return JsonResponse({"code": "0000", "message": "注冊成功", "data": token}) except: return JsonResponse({"code": "9999", "message": "用戶已注冊或信息缺失", "data": None}) class Login(View): def post(self, request): data = json.loads(request.body) username = data['username'] password = data['password'] # 校驗用戶名和密碼,成功返回user對象,失敗返回None user = authenticate(username=username, password=password) if user: token = create_token(user.username) return JsonResponse({"code": "0000", "message": "登錄成功", "data": token}) else: return JsonResponse({"code": "9999", "message": "用戶名或密碼不正確", "data": None})
-
Jmeter調用白名單、其他接口對比
- 踩坑:代碼中寫token是存在redis中的,所以一定要開啟redis服務。
白名單: '/middle_demo/signup/'
image白名單:'/middle_demo/login/'
image非白名單,沒有加token
image非白名單,加token
image
格式化響應輸出
Django中請求的生命周期
步驟
第一步:瀏覽器發起請求
第二步:WSGI創建socket服務端,接收請求(Httprequest)
第三步:中間件處理請求
第四步:url路由,根據當前請求的URL找到視圖函數
第五步:view視圖,進行業務處理(ORM處理數據,從數據庫取到數據返回給view視圖;view視圖將數據渲染到template模板;將數據返回)
第六步:中間件處理響應
第七步:WSGI返回響應(HttpResponse)
第八步:瀏覽器渲染
FBV模式和CBV模式(了解)
一個url對應一個視圖函數,這個模式叫做FBV(Function Base Views),即函數視圖
一個url對應一個類,這個模式叫做CBV(Class Base views),即類視圖
restframework框架
restful 規范
Restful API是目前比較成熟的一套互聯網應用程序的API設計理念,Rest是一組架構約束條件和原則,如何Rest約束條件和原則的架構,我們就稱為Restful架構,Restful架構具有結構清晰、符合標準、易于理解以及擴展方便等特點,受到越來越多網站的采用!
Restful API接口規范包括以下部分:
- 協議
API與用戶的通信協議,總是使用HTTPs協議。
- 域名
應該盡量將API部署在專用域名之下,如https://api.專屬域名.com
;如果確定API很簡單,不會有進一步擴展,可以考慮放在主域名下,如https://專屬域名.com/api/
。
- 版本
可以將版本號放在HTTP頭信息中,也可以放入URL中,如https://api.專屬域名.com/v1/
- 路徑
路徑是一種地址,在互聯網上表現為網址,在RESTful架構中,每個網址代表一種資源(resource),所以網址中不能有動詞,只能有名詞,而且所用的名詞往往與數據庫的表格名對應。一般來說,數據庫中的表都是同種記錄的"集合"(collection),所以API中的名詞也應該使用復數,如https://api.專屬域名.com/v1/students
。
- HTTP****動詞
對于資源的具體操作類型,由HTTP動詞表示,HTTP動詞主要有以下幾種,括號中對應的是SQL命令。
- GET(SELECT):從服務器取出資源(一項或多項);
- POST(CREATE):在服務器新建一個資源;
- PUT(UPDATE):在服務器更新資源(客戶端提供改變后的完整資源);
- PATCH(UPDATE):在服務器更新資源(客戶端提供改變的屬性);
- DELETE(DELETE):從服務器刪除資源;
- HEAD:獲取資源的元數據;
- OPTIONS:獲取信息,關于資源的哪些屬性是客戶端可以改變的。
- 過濾信息
如果記錄數量很多,服務器不可能都將它們返回給用戶,API會提供參數,過濾返回結果,常見的參數有:
- ?limit=20:指定返回記錄的數量為20;
- ?offset=8:指定返回記錄的開始位置為8;
- ?page=1&per_page=50:指定第1頁,以及每頁的記錄數為50;
- ?sortby=name&order=asc:指定返回結果按照name屬性進行升序排序;
- ?animal_type_id=2:指定篩選條件。
- 狀態碼
服務器會向用戶返回狀態碼和提示信息,以下是常用的一些狀態碼:
- 200 OK - [GET]:服務器成功返回用戶請求的數據;
- 201 CREATED - [POST/PUT/PATCH]:用戶新建或修改數據成功;
- 202 Accepted - [*]:表示一個請求已經進入后臺排隊(異步任務);
- 204 NO CONTENT - [DELETE]:用戶刪除數據成功;
- 400 INVALID REQUEST - [POST/PUT/PATCH]:用戶發出的請求有錯誤,服務器沒有進行新建或修改數據的操作;
- 401 Unauthorized - [*]:表示用戶沒有權限(令牌、用戶名、密碼錯誤);
- 403 Forbidden - [*] 表示用戶得到授權(與401錯誤相對),但是訪問是被禁止的;
- 404 NOT FOUND - [*]:用戶發出的請求針對的是不存在的記錄,服務器沒有進行操作;
- 406 Not Acceptable - [GET]:用戶請求的格式不可得;
- 410 Gone -[GET]:用戶請求的資源被永久刪除,且不會再得到的;
- 422 Unprocesable entity - [POST/PUT/PATCH] 當創建一個對象時,發生一個驗證錯誤;
- 500 INTERNAL SERVER ERROR - [*]:服務器發生錯誤,用戶將無法判斷發出的請求是否成功。
- 錯誤處理
如果狀態碼是4xx,就會向用戶返回出錯信息,一般來說,返回的信息中將error作為鍵名,出錯信息作為鍵值。
- 返回結果
針對不同操作,服務器向用戶返回的結果應該符合以下規范:
- GET /collection:返回資源對象的列表(數組);
- GET /collection/resource:返回單個資源對象;
- POST /collection:返回新生成的資源對象;
- PUT /collection/resource:返回完整的資源對象;
- PATCH /collection/resource:返回完整的資源對象;
- DELETE /collection/resource:返回一個空文檔。
- Hypermedia API
RESTful API最好做到Hypermedia,即返回結果中提供鏈接,連向其他API方法,使得用戶不查文檔,也知道下一步應該做什么。
以上是Restful API設計應遵循的十大規范,除此之外,Restful API還需注意身份認證應該使用OAuth 2.0框架,服務器返回的數據格式,應該盡量使用JSON,避免使用XML。