身份驗證是將傳入請求與一組標識憑據(例如請求來自的用戶或其簽名的令牌)相關聯的機制。而後 權限 和 限制 組件決定是否拒絕這個請求。前端
簡單來講就是:json
認證肯定了你是誰後端
權限肯定你能不能訪問某個接口app
限制肯定你訪問某個接口的頻率dom
REST framework 提供了一些開箱即用的身份驗證方案,而且還容許你實現自定義方案。ide
我的 敲碼log:函數
1.源碼分析
# Create your models here. class UserInfo1(models.Model): id = models.AutoField(primary_key=True) # 建立一個自增的主鍵字段 # 建立一個varchar(64)的惟一的不爲空的字段 name = models.CharField(null=False, max_length=20) pwd = models.CharField(max_length=32,default=123) class Token(models.Model): user = models.OneToOneField("UserInfo1",on_delete=models.CASCADE) token = models.CharField(max_length=128)
2.post
# 我須要一個隨機字符串 def get_random_str(user): import hashlib, time ctime = str(time.time()) # 封裝bytes類型一個MD5的加密對象 md5 = hashlib.md5(bytes(user, encoding="utf8")) # md5.update 是拼接的效果,隨機生成md5值 md5.update(bytes(ctime, encoding="utf8")) return md5.hexdigest() # 認證、權限和限制 class LoginModelView(APIView): def post(self, request): name = request.data.get("name") pwd = request.data.get("pwd") user = models.UserInfo1.objects.filter(name=name, pwd=pwd).first() res = {"state_code": 1000, "msg": None} if user: # 返回了一個usermd5 加密的字符串 random_str = get_random_str(user.name) """ 當存在token時,則更新,不存在則建立,defaults: 是由 (field, value) 對組成的字典,用於更新對象。 返回一個由 (object, created)組成的元組, object: 是一個建立的或者是被更新的對象, created: 是一個標示是否建立了新的對象的布爾值。 """ token = models.Token.objects.update_or_create(user=user, defaults={"token": random_str}) res["token"] = random_str else: res["state_code"] = 1001 # 錯誤狀態碼 res["msg"] = "用戶名或者密碼錯誤" import json # 這是由於json.dumps 序列化時對中文默認使用的ascii編碼.想輸出真正的中文須要指定ensure_ascii=False: # 若是你這樣的話就能把中文轉成json字符串,而不是 \u4e2d\u56fd return Response(json.dumps(res,ensure_ascii=False))
update_or_create(defaults=None, **kwargs)
defaults 的值不一樣則建立,相同則更新
例
Member.objects.update_or_create(defaults={'user':1}, others={'field1':1,'field2':1})
當存在user=1時,則更新,不存在則建立編碼
update_or_create(defaults=None, **kwargs)
kwargs: 來更新對象或建立一個新的對象。
defaults: 是由 (field, value) 對組成的字典,用於更新對象。
返回一個由 (object, created)組成的元組,
object: 是一個建立的或者是被更新的對象,
created: 是一個標示是否建立了新的對象的布爾值。
update_or_create: 方法經過給出的kwarg
好處:
1.他能判斷你是不是當前登陸用戶,當你沒有帶認證碼 向我後端發請求的時候我是不會給你數據的。
2.他每一次登陸的認證都會改變。
局部視圖認證
示例1:
class TokenAuth(object): def authenticate(self, request): # 取到 request裏面的 token值 totken = request.GET.get("token") token_obj = models.Token.objects.filter(token=totken).first() if not token_obj: # 拋認證字段的異常 raise exceptions.AuthenticationFailed("驗證失敗") else: return token_obj.user.name,token_obj.token def authenticate_header(self,request): pass
# 多條數據 class BookView(APIView): # 定義一個認證類 authentication_classes = [TokenAuth]
而沒有定義 token類的依然能夠訪問。
# 定義全局認證,全部視圖都須要認證 REST_FRAMEWORK = { "DEFAULT_AUTHENTICATION_CLASSES" : ["app01.utils.TokenAuth"] }
from app01 import models from rest_framework import exceptions class TokenAuth(object): def authenticate(self, request): # 取到 request裏面的 token值 totken = request.GET.get("token") token_obj = models.Token.objects.filter(token=totken).first() if not token_obj: # 拋認證字段的異常 raise exceptions.AuthenticationFailed("驗證失敗") else: return token_obj.user.name, token_obj.token def authenticate_header(self, request): pass
只有VIP用戶才能看的內容。
from rest_framework.permissions import BasePermission class SVIPPermission(BasePermission): message="只有超級用戶才能訪問" def has_permission(self,request,view): username=request.user user_type=User.objects.filter(name=username).first().user_type if user_type==3: return True # 經過權限認證 else: return False
# 封裝了3層 class AuthorDetaiView(viewsets.ModelViewSet): permission_classes = [SVIPpermission,] throttle_classes = [] # 限制某個ip 每分鐘訪問次數不能超過20次 # authentication_classes = [TokenAuth] # queryset serializer 這兩個方法必定要定義成這個否則取不到值 queryset = models.Author.objects.all() serializer_class = AuthorModelSerializers
# 自定義局部限制
import time # 自定義限制 VISIT_RECORD = {} class VisitRateThrottle(object): def __init__(self): self.history = None def allow_request(self, request, view): """ 自定義頻率限制60秒內只能訪問三次 """ # 獲取用戶IP ip = request.META.get("REMOTE_ADDR") # 獲取當前時間戳 timestamp = time.time() # 若是當前訪問ip沒有在列表中 我就新建一個IP訪問記錄 if ip not in VISIT_RECORD: VISIT_RECORD[ip] = [timestamp, ] # 能夠經過驗證 return True # 若是列表中有值,我就取噹噹前ip地址 賦值給變量 history = VISIT_RECORD[ip] self.history = history # 在列表頭部 插入一個時間戳 history.insert(0, timestamp) # 若是列表有值,最早插入的值小於 當前值-60 ,tiemstamp是當前時間是在增長的 while history and history[-1] < timestamp - 60: # pop 取出 最後一個條件成立的值 history.pop() # 列表中的時間戳 大於3 就返回falsse,不然經過 if len(history) > 3: return False else: return True def wait(self): # 返回給前端還剩多少時間能夠訪問 timestamp = time.time() # 求出還剩多少時間能夠訪問 return 60 - (timestamp - self.history[-1])
# 視圖使用
from rest_framework.response import Response class AuthorModelView(viewsets.ModelViewSet): authentication_classes = [TokenAuth, ] permission_classes = [SVIPPermission, ] throttle_classes = [VisitRateThrottle, ] # 限制某個IP每分鐘訪問次數不能超過20次 queryset = Author.objects.all() serializer_class = AuthorModelSerializers # 分頁 # pagination_class = MyPageNumberPagination # renderer_classes = []
# 全局使用
# 在settings.py中設置rest framework相關配置項 REST_FRAMEWORK = { "DEFAULT_AUTHENTICATION_CLASSES": ["app01.utils.TokenAuth", ], "DEFAULT_PERMISSION_CLASSES": ["app01.utils.SVIPPermission", ], "DEFAULT_THROTTLE_CLASSES": ["app01.utils.MyThrottle", ], }
定義頻率類,必須繼承SimpleRateThrottle類:
from rest_framework.throttling import SimpleRateThrottle class VisitThrottle(SimpleRateThrottle):
# 必須配置scope參數 經過scope參數去settings中找頻率限定的規則 scope = 'throttle'
# 必須定義 get_cache_key函數 返回用戶標識的key 這裏借用源碼中BaseThrottle類(SimpleRateThrottle的父類)中的get_ident函數返回用戶ip地址 def get_cache_key(self, request, view): return self.get_ident(request)
局部使用:
視圖函數中加上
throttle_classes = [VisitThrottle,]
全局使用:settings中配置
REST_FRAMEWORK={ 'DEFAULT_THROTTLE_CLASSES':['utils.common.VisitThrottle'],
# 局部使用也要在settings中配置上 DEFAULT_THROTTLE_RATES 經過self.scope取頻率規則 (一分鐘訪問3次) 'DEFAULT_THROTTLE_RATES':{'throttle':'3/m',} }
設置錯誤信息爲中文:
class Course(APIView): authentication_classes = [TokenAuth, ] permission_classes = [UserPermission, ] throttle_classes = [MyThrottles,] def get(self, request): return HttpResponse('get') def post(self, request): return HttpResponse('post') # 函數名爲throttled 重寫Throttled類中默認的錯誤信息 def throttled(self, request, wait): from rest_framework.exceptions import Throttled class MyThrottled(Throttled): default_detail = '訪問頻繁' extra_detail_singular = '等待 {wait} second.' extra_detail_plural = '等待 {wait} seconds.' raise MyThrottled(wait)