drf : 認證 頻率 許可權
阿新 • • 發佈:2022-04-11
編寫登入功能引出認證,許可權,頻率:
前端通過介面測試工具Postman將使用者名稱和密碼通過HTTP請求傳送至Django框架
models.py
from django.db import models # Create your models here. class Books(models.Model): name = models.CharField(max_length=32) price = models.IntegerField() publish = models.ForeignKey(to='Publish', on_delete=models.DO_NOTHING) class Publish(models.Model): name = models.CharField(max_length=32) addr = models.CharField(max_length=32) class User(models.Model): username = models.CharField(max_length=32) password = models.CharField(max_length=32) """ 使用者登入生成隨機字串,響應中攜帶字串,傳送至服務端後,服務端做邏輯判斷。 一般一對一為垂直分表。資料庫優化的手段 """ class UserToken(models.Model): user = models.OneToOneField(to='User', on_delete=models.CASCADE) token = models.CharField(max_length=64)
模型層拓展:
欄位引數可以填寫函式名,但不是不能加括號。
views.py
from rest_framework.views import APIView from . import models import uuid from rest_framework.response import Response class UserAPIView(APIView): def post(self,request,*args,**kwargs): back_dic = {'code':200,'msg':''} username = request.data.get('username') password = request.data.get('password') # 過濾出user表中含有請求中攜帶的資料,username,password. user = models.User.objects.filter(username=username,password=password).first() print(user) if user: """ 有使用者的情況下 登入成功之後生成token user=user,根據user查詢,查詢不到使用defaults新增,查詢到更新。 """ # 生成隨機字串,請求中攜帶隨機字元傳 token = str(uuid.uuid4()) models.UserToken.objects.update_or_create(defaults={'token': token}, user=user) back_dic['msg'] = '隨機字串更新' back_dic['token'] = token else: back_dic['code'] = 201 back_dic['msg'] = '使用者名稱或者密碼錯誤' return Response(back_dic)
此時客戶端傳送post請求,後端驗證正確會產生一條新的token覆蓋原有的token。
認證,許可權,頻率所在位置。
APIView --> dispath(重寫了request方法) --> initial(認證,許可權,頻率) def initial(self, request, *args, **kwargs): """ Runs anything that needs to occur prior to calling the method handler. """ self.format_kwarg = self.get_format_suffix(**kwargs) # Perform content negotiation and store the accepted info on the request neg = self.perform_content_negotiation(request) request.accepted_renderer, request.accepted_media_type = neg # Determine the API version, if versioning is in use. version, scheme = self.determine_version(request, *args, **kwargs) request.version, request.versioning_scheme = version, scheme # Ensure that the incoming request is permitted """認證""" self.perform_authentication(request) """許可權""" self.check_permissions(request) """頻率""" self.check_throttles(request)
認證類的編寫
之前提到過,如果程式碼不屬於檢視層就另開一個py檔案。
認證:只有攜帶token隨機字串才可以訪問某個路由。
urls.py
from app01 import views
from rest_framework.routers import SimpleRouter, DefaultRouter
from django.urls import path, include
router = SimpleRouter()
router.register('books', views.BookAPIView)
urlpatterns = [
# path('books/',views.BookAPIView.as_view({'get':'login'})),
path('', include(router.urls)),
path('login/', views.UserAPIView.as_view())
]
views.py
from .auth import LoginAuth
from rest_framework.generics import ListAPIView
from rest_framework.viewsets import ViewSetMixin
class BookAPIView(ViewSetMixin, ListAPIView):
queryset = Books.objects.all()
serializer_class = BookSerializers
# 增加登入認證
authentication_classes = [LoginAuth]
auth.py
"""認證類"""
from rest_framework.authentication import BaseAuthentication
from . import models
from rest_framework.exceptions import AuthenticationFailed
class LoginAuth(BaseAuthentication):
def authenticate(self,request):
# 寫認證規則
# 取出使用者攜帶的token
# -注意:取get提交的資料,不要從request.GET中取,從request.query_params中取
token = request.query_params.get('token')
print(token)
# 去資料庫查詢,token是否存在,存在表示登入了,不存在表示為登入
user_token = models.UserToken.objects.filter(token=token).first()
if user_token:
# 943ffc46-3826-47f9-a4de-ea9cd4bd23d6
print(user_token.token)
# User object (1),模型類物件
print(user_token.user)
# junjie
print(user_token.user.username)
return user_token.user,user_token.token
raise AuthenticationFailed('您沒有登入,認證失敗')
此時想要訪問books必須攜帶token才可以訪問。
認證類的全域性和區域性使用
全域性配置:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES':[
'app01.auth.LoginAuth'
]
}
區域性禁用配置:
class UserAPIView(APIView):
authentication_classes = []
原始碼分析:
# APIView--->dispathch方法的-----》self.initial(request, *args, **kwargs)(497行)---》APIView的initial方法----》有三句話
self.perform_authentication(request) # 認證
self.check_permissions(request) # 許可權
self.check_throttles(request) # 頻率
# self.perform_authentication(request)----》新的request物件的.user(是個方法,包裝成了資料數屬性)---》新的Request類中找到了user方法---》self._authenticate()-----》Request類中的_authenticate
def _authenticate(self):
for authenticator in self.authenticators: # 配置在檢視類中認證類列表,例項化得到的物件
try:
user_auth_tuple = authenticator.authenticate(self)
except exceptions.APIException:
self._not_authenticated()
raise
if user_auth_tuple is not None:
self._authenticator = authenticator
self.user, self.auth = user_auth_tuple
return
# self.authenticators,self是Request類的物件,authenticators屬性是Request這個類在例項化的時候,傳入的
# Request類在例項化的時候程式碼
return Request(
request,
parsers=self.get_parsers(),
authenticators=self.get_authenticators(), # 要看的
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)
# self.get_authenticators()是APIView的方法
def get_authenticators(self):
# 列表推導式
return [auth() for auth in self.authentication_classes]
# 返回結果是我們配在檢視類中自己寫的認證類列表的物件
return [LoginAuth(),]
許可權元件
匯入模組
from rest_framework.permissions import BasePermission
auth.py
from rest_framework.permissions import BasePermission
class UserPermission(BasePermission):
def has_permission(self, request, view):
"""
認證類走完再走許可權類,所以request.user可以拿到當前登入使用者
"""
if request.user.user_type == 1:
return True
return False
全域性配置使用:
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
'app01.auth.UserPermission',
],
}
區域性使用:
class BookAPIView(ViewSetMixin, ListAPIView):
queryset = Books.objects.all()
serializer_class = BookSerializers
# 增加登入認證
authentication_classes = [LoginAuth]
# 增加許可權類
permission_classes = [UserPermission]
區域性禁用:
permission_classes = []
許可權類原始碼解讀
APIView --> dispath --> initial --> self.check_permissions(request) --》
def check_permissions(self, request):
"""
for permission in self.get_permissions(),self.get_permissions()在哪?
通過原始碼可以看到是許可權類的全域性和區域性配置。區域性有優先區域性,區域性沒有定義取全域性,全域性沒有去內建。
def get_permissions(self):
return [permission() for permission in self.permission_classes]
"""
for permission in self.get_permissions():
"""
對應到auth.py中的許可權類,如果為True,程式碼正常執行,如果為False,執行self.permission_denied
"""
if not permission.has_permission(request, self):
self.permission_denied(
request,
"""
反射,現在auth.py中的許可權類找massage.
"""
message=getattr(permission, 'message', None),
code=getattr(permission, 'code', None)
)
# message 自定義報錯資訊
def permission_denied(self, request, message=None, code=None):
"""
If request is not permitted, determine what kind of exception to raise.
"""
if request.authenticators and not request.successful_authenticator:
raise exceptions.NotAuthenticated()
raise exceptions.PermissionDenied(detail=message, code=code)
auth.py
class UserPermission(BasePermission):
def has_permission(self, request, view):
"""
認證類走完再走許可權類,所以request.user可以拿到當前登入使用者
"""
if request.user.user_type == 1:
return True
return False
頻率元件的使用
頻率的作用: 可以對介面訪問的頻次進行限制,以減輕伺服器壓力。一般用於付費購買次數,投票等場景使用.
**匯入模組: **
from rest_framework.throttling import SimpleRateThrottle
auth.py,寫一個類,繼承基類,重寫get_cache_key方法
class MyThrottle(SimpleRateThrottle):
# scope一定要寫,字串對應到settings.py中配置REST_FRAMEWORK。
scope = 'ip_Throttle'
def get_cache_key(self, request, view):
# 請求頭所包含的資訊在META中
# 127.0.0.1
print(request.META.get('REMOTE_ADDR'))
return request.META.get('REMOTE_ADDR')
settings.py,配置檔案中一定要配置。
REST_FRAMEWORK = {
# (一分鐘訪問三次),key要跟類中的scope對應
'DEFAULT_THROTTLE_RATES':{
'ip_Throttle':'3/m'
}
}
settings.py ,全域性配置。
REST_FRAMEWORK = {
# (一分鐘訪問三次),key要跟類中的scope對應
'DEFAULT_THROTTLE_RATES':{
'ip_Throttle':'3/m'
},
# 全域性配置
'DEFAULT_THROTTLE_CLASSES': (
'app01.auth.MyThrottle',
),
}
views.py,檢視類中配置
class BookAPIView(ViewSetMixin, ListAPIView):
# 區域性使用
throttle_classes = [MyThrottle,]