1. 用戶登錄認證
-
a. 有些API需要用戶登錄成功之後,才能訪問;有些無需登錄就能訪問。
-
b. 基本使用認證組件
解決思路:
a. 創建兩張表(用戶表,token表)
b. 用戶登錄(返回token並保存到數據庫) -
c.涉及兩種操作(全局認證,單獨認證)
-
e. 使用框架內置認證類(自己寫認證類的時候繼承自BaseAuthentication這個類,更規範,不用自己從頭搞)
-
- 認證類,必須繼承:
from rest_framework.authentication import BaseAuthentication
- 認證類,必須繼承:
-
- 其他認證類:
BasicAuthentication
(可以瀏覽器自動生成非模態對話框的一種認證類,SessionAuthentication
等等,這些都是基於BaseAuthentication
瀏覽器幫助加密,放到請求頭裏邊傳過去得到,基本用不到)
- 其他認證類:
- 3.大多場景下,繼承自
BaseAuthentication
,然後自己定義認證類
基類如下:
-
class BaseAuthentication(object):
"""
All authentication classes should extend BaseAuthentication.
"""
def authenticate(self, request):
"""
Authenticate the request and return a two-tuple of (user, token).
"""
raise NotImplementedError(".authenticate() must be overridden.")
def authenticate_header(self, request): # 認證失敗時,返回的響應頭的定義
"""
Return a string to be used as the value of the `WWW-Authenticate`
header in a `401 Unauthenticated` response, or `None` if the
authentication scheme should return `403 Permission Denied` responses.
"""
pass
認證梳理:
1. 使用
-
創建類:繼承
BaseAuthentication
; 實現:authenticate
方法,其中authenticate_header
方法,不用寫,直接按基類pass就行 -
authenticate
的返回值:- 1.返回
None
,跳過,讓下一認證來執行。 - 2.返回(拋出)異常(這裏上邊類有隊異常的接收跟處理)
raise exceptions.AuthenticationFailed
(用戶認證失敗from rest_framework import exceptions
) - 3.返回一個元組(元素1,元素2) # 元素1賦值給
request.user
; 元素2賦值給request.auth
,可以在views.py
視圖中對返回的兩個,進行調用,比如拿到驗證通過的對象user
(前提定義函數時,得返回user相關的信息)
- 1.返回
-
局部使用(局部針對某些類進行驗證,針對單獨的類添加靜態字段)
from rest_framework.authentication import BaseAuthentication,BasicAuthentication
class UserInfoView(APIView):
"""
訂單相關業務
"""
authentication_classes = [BasicAuthentication,] # 設置這靜態字段,單獨設置
def get(self,request,*args,**kwargs):
print(request.user)
return HttpResponse('用戶信息')
- 全局使用(在settings裏邊設置配置文件):
- 全局使用時的文件爲路徑字符串,如下;而在局部用時,直接添加驗證類的名字就行。
REST_FRAMEWORK = {
# 全局使用的認證類
```python
REST_FRAMEWORK = {
# 全局使用的認證類
"DEFAULT_AUTHENTICATION_CLASSES":['api.utils.auth.FirstAuthtication','api.utils.auth.Authtication', ], #裏邊寫的類文件的位置
# "UNAUTHENTICATED_USER":lambda :"匿名用戶"
"UNAUTHENTICATED_USER":None, # 匿名,request.user = None
"UNAUTHENTICATED_TOKEN":None,# 匿名,request.auth = None
}
2. 源碼流程
- dispatch
- 封裝request
- 獲取定義的認證類(全局/局部),通過列表生成時創建對象。
- initial
- perform_authentication
request.user(內部循環…)
- user
- _authenticate(定義返回內容,有返回元組,沒有,返回個元組)
- perform_authentication
- 封裝request
2. 權限
class MyPermission(object):
def has_permission(self,request,view):
if request.user.user_type != 3:
return False
return True
class OrderView(APIView):
"""
訂單相關業務(只有SVIP用戶有權限)
"""
permission_classes = [MyPermission,]
def get(self,request,*args,**kwargs):
# request.user
# request.auth
self.dispatch
ret = {'code':1000,'msg':None,'data':None}
try:
ret['data'] = ORDER_DICT
except Exception as e:
pass
return JsonResponse(ret)
權限知識梳理:
同上,爲了代碼規範,最好也繼承自BasePermission
- 類,必須繼承:
BasePermission
,必須實現:has_permission
方法,其中還有個has_object_persion
方法,用來細化到針對對象的權限判斷
from rest_framework.permissions import BasePermission
class SVIPPermission(BasePermission):
message = "必須是SVIP才能訪問"
def has_permission(self,request,view):
if request.user.user_type != 3:
return False
return True
- 返回值:
- True, 有權訪問
- False,無權訪問
- 局部使用
class UserInfoView(APIView):
"""
訂單相關業務(普通用戶、VIP)
"""
permission_classes = [MyPermission1, ]
def get(self,request,*args,**kwargs):
return HttpResponse('用戶信息')
- 全局使用
REST_FRAMEWORK = {
"DEFAULT_PERMISSION_CLASSES":['api.utils.permission.SVIPPermission']
}
3.訪問頻率控制(節流/限流)
控制請求的訪問頻率,具體實現
代碼實現是拿ip來記錄,對於沒登錄的匿名用戶,我們能拿到的也只有ip,要是換ip訪問,這個控制不了
訪問的ip地址的獲得方式 :
request.META.get('REMOTE_ADDR)
- 1.類, 若繼承:BaseThrottle,要自己實現:allow_request、wait
- 2.類, 若繼承:SimpleRateThrottle,只需實現實現:get_cache_key、其中的一個字段:scope = “Luffy”(配置文件中限制節流頻率的key)
import time
VISIT_RECORD = {}
class VisitThrottle(object):
"""60s內只能訪問3次"""
def __init__(self):
self.history = None
def allow_request(self,request,view):
# 1. 獲取用戶IP
remote_addr = request.META.get('REMOTE_ADDR')
ctime = time.time()
if remote_addr not in VISIT_RECORD:
VISIT_RECORD[remote_addr] = [ctime,]
return True
history = VISIT_RECORD.get(remote_addr)
self.history = history
while history and history[-1] < ctime - 60:
history.pop()
if len(history) < 3:
history.insert(0,ctime)
return True
# return True # 表示可以繼續訪問
# return False # 表示訪問頻率太高,被限制
def wait(self):
"""
還需要等多少秒才能訪問
:return:
"""
ctime = time.time()
return 60 - (ctime - self.history[-1])
class AuthView(APIView):
"""
用於用戶登錄認證
"""
authentication_classes = []
permission_classes = []
throttle_classes = [VisitThrottle,]
def post(self,request,*args,**kwargs):
ret = {'code':1000,'msg':None}
try:
user = request._request.POST.get('username')
pwd = request._request.POST.get('password')
obj = models.UserInfo.objects.filter(username=user,password=pwd).first()
if not obj:
ret['code'] = 1001
ret['msg'] = "用戶名或密碼錯誤"
# 爲登錄用戶創建token
token = md5(user)
# 存在就更新,不存在就創建
models.UserToken.objects.update_or_create(user=obj,defaults={'token':token})
ret['token'] = token
except Exception as e:
ret['code'] = 1002
ret['msg'] = '請求異常'
return JsonResponse(ret)
源碼流程梳理
from rest_framework.throttling import BaseThrottle, SimpleRateThrottle
# 代碼中的get_ident()獲得標識,不僅可以用遠程IP,或者自己寫的標識,也可以獲得傳輸過來的代理的Ip,但是前提是請求中必須包含過來的ip數據,不然也是無法實現的
class MyThrottle(SimpleRateThrottle):
"""限制所有用戶"""
scope = 'Luffy'
def get_cache_key(self, request, view):
#設置的進行頻率限制時的唯一標識。緩存的鍵,可以設置遠程ip爲標識,利用get_ident()
return self.get_ident(request)
class UserThrottle(SimpleRateThrottle):
"""限制已經登錄用戶的訪問頻率"""
scope = 'LuffyUser'
def get_cache_key(self, request, view):
#設置進行頻率限制的唯一標識。緩存的鍵,可設置遠程ip爲標識,利用get_ident()
return request.user.username
- 全局使用
REST_FRAMEWORK = {
REST_FRAMEWORK = {
# 全局使用的認證類
"DEFAULT_AUTHENTICATION_CLASSES":['api.utils.auth.FirstAuthtication','api.utils.auth.Authtication', ],
# "DEFAULT_AUTHENTICATION_CLASSES":['api.utils.auth.FirstAuthtication', ],
# "UNAUTHENTICATED_USER":lambda :"匿名用戶"
"UNAUTHENTICATED_USER":None, # 匿名,request.user = None
"UNAUTHENTICATED_TOKEN":None,# 匿名,request.auth = None
"DEFAULT_PERMISSION_CLASSES":['api.utils.permission.SVIPPermission'],
# 這裏設置的是針對所有用戶頻率的限制,針對單獨的爲登錄用戶的設置可以直接在auth認證那裏
# 直接添加字段;如果頻率一樣的話,可以直接針對ip進行全局的限制。
"DEFAULT_THROTTLE_CLASSES":["api.utils.throttle.UserThrottle"],
"DEFAULT_THROTTLE_RATES":{
"Luffy":'3/m', # 3次每分鐘(SimpleRateThrottle內部方法實現)
"LuffyUser":'10/m', #
}
}
- 局部單獨使用,直接加導視圖函數內就行
throttle_classes = [UserThrottle,]
(見上方代碼)