drf--request认证+权限+频率+自定义频率类+内置(认证,权限,频率)类+鸭子类型+补充(模型类choices参数+request补充+settings文件导包)

10.9内容整理和概述

今日内容概要

内容目录

  • request--认证
  • request--权限
  • request--频率
  • 自定义频率类
  • 内置认证类,权限类,频率类
  • 鸭子类型
  • 战略补充

request--认证

应用背景:访问接口时,我们需要对用户的身份进行认证,符合要求的才允许访问	# 比如登录

步骤:
	1.新建py文件,用于编写编写认证类
	2.导包:from rest_framework.authentication import BaseAuthentication
	3.认证类继承BaseAuthentication类
	4.需要重写authenticate方法,并传入request参数	# 例子: def authenticate(self, request): pass
	5.在authenticate方法写入认证逻辑
	6.认证通过返回两个数据(登录用户+标记数据);不通过抛认证类异常AuthenticationFailed	# 认证类异常导包:from rest_framework.exceptions import AuthenticationFailed————继承于APIException

认证类的部署:
	1.全局配置:
		步骤:
			1.在app的settings.py文件中修改REST_FRAMEWORK属性,添加DEFAULT_AUTHENTICATION_CLASSES属性
			2.DEFAULT_AUTHENTICATION_CLASSES属性对应数据为列表套类名
		例子:
			REST_FRAMEWORK={
				'DEFAULT_AUTHENTICATION_CLASSES':['app01.auth.LoginAuth',]
			}
	2.局部部署:
		步骤:
			1.在视图类的py文件中导入认证类
			2.在需要认证的视图类中添加authentication_classes属性
			3.authentication_classes属性对应数据类型为列表,列表元素为认证类	# 例子:authentication_classes = [LoginAuth,]
	3.局部禁用:
		应用背景:在全局部署认证的情况下,对单独视图类解放认证
		步骤:
			1.在需要解放认证的视图类中添加authentication_classes属性
			2.令authentication_classes属性等于空列表	# 例子:authentication_classes = []

'--------------认证类源码分析----------------'
执行流程:	
	1.视图类继承APIView,通过路由匹配调用as_view方法
	2.当前类中没有as_view方法,去调用父类APIView的as_view方法,取消了csrf认证,并调用View类的as_view方法
	3.View类的as_view方法调用APIView类的dispatch方法
	4.dispatch方法中包装了新的request,并执行APIView类的initial方法对request进行认证,频率,权限	# 源码:self.initial(request, *args, **kwargs)
	5.perform_authentication方法是APIView类的initial方法中对认证的实现	# 源码:self.perform_authentication(request)
	6.perform_authentication方法执行了request.user		# 当前request是包装过的新request
	7.去Request类中调用user方法
		功能介绍:通过反射机制判断执行次数,第一次调用时执行_authenticate方法,之后直接返回self._user
		源码: 
			def user(self):
				if not hasattr(self, '_user'): # Request类的对象中反射_user
					with wrap_attributeerrors():
					self._authenticate()  # 第一次会走这个代码
				return self._user
	8.第一次调用执行Request类的_authenticate方法
		执行流程:
			1.for循环视图类中的认证类列表		# 源码:for authenticator in self.authenticators
			2.每次循环调用对应视图类的写的认证方法--authenticate方法,认证通过用一个元组接收返回结果;认证失败抛异常	# 源码:user_auth_tuple = authenticator.authenticate(self)
			3.如果元组有值,解压赋值给新的request的user和auth属性		# 源码:self.user, self.auth = user_auth_tuple
			4.解压赋值成功,执行return		# 说明在视图类的认证部署中可以部署多个认证类,但是只要成功一个,后续认证类将不再执行认证功能
		大体源码:
			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

request--认证类例子

from .models import UserToken
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed

class LoginAuth(BaseAuthentication):
    def authenticate(self, request):
        # 在这里做认证,校验用户是否登录(带了token,并且能查到,就是登录,返回两个值,否则就是没登录,抛异常)
        # 用户带的token从哪取?后端人员定的:放在请求地址中
        token = request.GET.get('token')
        # 通过token查询该token是否是在表中有记录
        user_token = UserToken.objects.filter(token=token).first()
        if user_token:
            return user_token.user, token  # 返回两个值,一个是当前登录用户,一个是token
        else:
            raise AuthenticationFailed('您没有登录')

request--权限

应用背景:通过认证的用户,也可以通过权限的不同,进行不同API接口的使用

步骤:
	1.新建py文件,用于编写编写权限类
	2.导包:from rest_framework.permissions import BasePermission
	3.权限类继承BasePermission类
	4.需要重写has_permission方法,并传入request,view参数	# 例子: def has_permission(self, request, view): pass
	5.在has_permission方法写入权限逻辑
	6.认证通过返回True;不通过返回False
	7.message参数:当权限不通过时,系统会返回message参数的提示信息数据给前端

权限类的部署:
	1.全局配置:
		步骤:
			1.在app的settings.py文件中修改REST_FRAMEWORK属性,添加DEFAULT_PERMISSION_CLASSES属性
			2.DEFAULT_PERMISSION_CLASSES属性对应数据为列表套类名
		例子:
			REST_FRAMEWORK={
				'DEFAULT_PERMISSION_CLASSES':['app01.permission.UserTypePermission',]
			}
	2.局部部署:
		步骤:
			1.在视图类的py文件中导入权限类
			2.在需要权限的视图类中添加permission_classes属性
			3.permission_classes属性对应数据类型为列表,列表元素为权限类	# 例子:permission_classes = [UserTypePermission,]
	3.局部禁用:
		应用背景:在全局部署权限的情况下,对单独视图类解放权限
		步骤:
			1.在需要解放权限的视图类中添加permission_classes属性
			2.令permission_classes属性等于空列表	# 例子:permission_classes = []

'--------------权限类源码分析----------------'
执行流程:	
	1.视图类继承APIView,通过路由匹配调用as_view方法
	2.当前类中没有as_view方法,去调用父类APIView的as_view方法,取消了csrf认证,并调用View类的as_view方法
	3.View类的as_view方法调用APIView类的dispatch方法
	4.dispatch方法中包装了新的request,并执行APIView类的initial方法对request进行认证,频率,权限	# 源码:self.initial(request, *args, **kwargs)
	5.check_permissions方法是APIView类的initial方法中对权限的实现	# 源码:self.check_permissions(request)
	6.APIView类的check_permissions方法执行流程:		
		1.for循环视图类中的权限类列表	# 源码:for permission in self.get_permissions():
		2.每次循环调用权限类对象的has_permission方法,并传入参数(权限类的对象,request,视图类的对象),返回布尔类型数据做if判断	# 源码:if not permission.has_permission(request, self):
		3.如果if判断为True,权限通过
		3.如果if判断为False,权限不通过,调用视图类的permission_denied方法通过反射机制给参数(request,message,code)赋值

总结:视图类的权限部署中可以部署多个权限类,都会执行

request--权限例子

from rest_framework.permissions import BasePermission
class UserTypePermission(BasePermission):
	def has_permission(self, request, view):
		if request.user.user_type == 1:
			return True  
		else:
			self.message = '您是:%s 用户,您没有权限'%request.user.get_user_type_display()
			return False 

request--频率

应用背景:限定用户在规定时间的访问次数,无论是否通过认证和权限

步骤:
	1.新建py文件,用于编写编写频率类
	2.导包:from rest_framework.throttling import BaseThrottle, SimpleRateThrottle		# 建议继承SimpleRateThrottle类,封装程度高,开发效率高
	3.频率类继承SimpleRateThrottle类
	4.重写get_cache_key方法,传入(self, request, view)参数,暂时不用写方法逻辑		# 例子: def get_cache_key(self, request, view):
	5.返回一个频率限制的唯一标志,比如用户id,ip地址		# 例子:return request.META.get('REMOTE_ADDR'):返回客户端的ip地址
	6.添加scope属性		# 例子:scope = 'luffy'
	7.到app的settings文件中做scope属性对应,定义频率	
		步骤:
            1.在REST_FRAMEWORK属性中添加DEFAULT_THROTTLE_RATES属性,对应数据类型为字典
            2.DEFAULT_THROTTLE_RATES属性中添加键值对('scope对应数据': '频率' )
		例子:
			REST_FRAMEWORK = {
				'DEFAULT_THROTTLE_RATES': {
					'luffy': '3/m'  # 3/h  3/s  3/d
					}
			}

频率类的部署:
	1.全局配置:
		步骤:
			1.在app的settings.py文件中修改REST_FRAMEWORK属性,添加DEFAULT_THROTTLE_CLASSES属性
			2.DEFAULT_THROTTLE_CLASSES属性对应数据为列表套类名
		例子:
			REST_FRAMEWORK={
				'DEFAULT_THROTTLE_CLASSES':['app01.throttling.MyThrottling'],
			}
	2.局部部署:
		步骤:
			1.在视图类的py文件中导入频率类
			2.在需要频率的视图类中添加throttle_classes属性
			3.throttle_classes属性对应数据类型为列表,列表元素为频率类	# 例子:throttle_classes = [MyThrottling,]
	3.局部禁用:
		应用背景:在全局部署频率的情况下,对单独视图类解放频率
		步骤:
			1.在需要解放频率的视图类中添加throttle_classes属性
			2.令throttle_classes属性等于空列表	# 例子:throttle_classes = []	

'--------------频率类源码分析----------------'
执行流程:		# 继承SimpleRateThrottle类版本
	1.视图类继承APIView,通过路由匹配调用as_view方法
	2.当前类中没有as_view方法,去调用父类APIView的as_view方法,取消了csrf认证,并调用View类的as_view方法
	3.View类的as_view方法调用APIView类的dispatch方法
	4.dispatch方法中包装了新的request,并执行APIView类的initial方法对request进行认证,频率,权限	# 源码:self.initial(request, *args, **kwargs)
	5.check_throttles方法是APIView类的initial方法中对频率的实现	# 源码:self.check_throttles(request)
	6.APIView类的check_throttles方法执行流程:		
		1.for循环视图类中的频率类列表	# 源码:for throttle in self.get_throttles():
		2.1 每次循环调用SimpleRateThrottle类的allow_request方法(因为当前频率类没有重写allow_request方法),并传入参数(SimpleRateThrottle类的对象,request,视图类的对象),返回布尔类型数据做if判断	# 源码:if not throttle.allow_request(request, self):
			'---SimpleRateThrottle类的allow_request方法源码---':
					1.先对self.rate做判断,如果为空,返回True,频率结束(不做频率校验)
						1.1 self.rate先判断当前频率对象是否有'rate'属性,如果有,执行1.3;如果没有,执行get_rate方法
						1.2 get_rate方法属于SimpleRateThrottle类方法,反射'scope'属性并做判断,如果没值,抛异常;如果有值,则返回self.THROTTLE_RATES[self.scope]
							1.2.1 self.THROTTLE_RATES属性为settings文件中REST_FRAMEWORK属性里配置的'DEFAULT_THROTTLE_CLASSES'对应的数据列表
							1.2.2 让后通过字典将scope属性对应的value值取出来,返回出去,再由步骤1.1的self.rate接收
						1.3 调用SimpleRateThrottle类的parse_rate方法对self.rate数据进行切割,加工返回一个元组(num_requests, duration)在解压赋值给self.num_requests,self.duration
							1.3.1 判断rate是否为空,如果为空,返回空元组
							1.3.2 通过'/'进行切割,解压赋值给num, period
							1.3.3 将num转为整型赋予num_requests
							1.3.4 根据给定字典拿取period首字母对应的数据,赋予duration		# 字典:{'s': 1, 'm': 60, 'h': 3600, 'd': 86400}
							1.3.5 将元组(num_requests, duration)返回
					2.调用在频率类重写的get_cache_key方法,将返回结果赋给self.key,并做判断,如果self.key为空,返回True,频率结束(不做频率校验)
					3.从缓存中拿取访问时间列表,赋予self.history,如果缓存中没有,则让self.history为空列表
					4.将当前时间赋予self.now
					5.循环判断访问时间列表self.history中是否有超过了频率校验规定时间的数据,如果有,将它删除
					6.判断循环完后的self.history长度是否大于等于规定访问次数self.num_requests
					7.如果大于等于,返回False,频率校验结束
					8.如果小于,返回True,频率校验结束
		2.2 如果继承BaseThrottle类的频率类,则需要重写allow_request方法,for循环每次循环便会调用频率类重写的allow_request方法,进行频率验证
			'---自定义频率类的allow_request方法(见下方)---'
		3.如果if判断为True,频率达标
		3.如果if判断为False,权限不达标,调用throttle_durations.append(throttle.wait())执行频率限制

总结:视图类的权限部署中可以部署多个权限类,都会执行

request--频率例子

from rest_framework.throttling import BaseThrottle, SimpleRateThrottle
class MyThrottling(SimpleRateThrottle):  # 我们继承SimpleRateThrottle去写,而不是继承BaseThrottle去写
    scope = 'luffy'
    def get_cache_key(self, request, view):
        return request.META.get('REMOTE_ADDR')

自定义频率类

应用背景:我们想自己编辑频率逻辑,不借用SimpleRateThrottle类

步骤:
	1.新建py文件,用于编写编写频率类
	2.导包:from rest_framework.throttling import BaseThrottle		# 继承与BaseThrottle类
	3.重写allow_request方法,写频率校验逻辑
		1.取出访问者的唯一标识	# 例子:取出访问者ip地址--ip = request.META.get('REMOTE_ADDR')
		2.取出当前时间
		3.定义新的类属性VISIT_RECORD,用于存放用户访问记录,数据类型为字典套列表
		4.判断当前用户是否存在在VISIT_RECORD属性中
		5.如果存在,添加记录并直接返回True,不走下面逻辑
		6.如果不存在,则从VISIT_RECORD属性中取出用户对应的访问记录数据列表,赋予self.history		# self.history在对象初始化配置--def __init__(self):self.history = None
		7.循环访问记录列表,如果数据符合频率限制要求,就将其删除
		8.判断self.history长度是否超过频率限制次数,超过返回False;未超过,想self.history访问记录数据列表头部插入当前访问时间,并返回True
	4.重写wait方法,用于前端提示用户待访问时间

代码演示:
	from rest_framework.throttling import BaseThrottle
	class MyThrottle(BaseThrottle):
		VISIT_RECORD = {}	# 存放用户访问记录,例子:{ip1:[时间1,时间2],ip2:[时间1,时间2],}
        
		def __init__(self):
			self.history = None
            
		def allow_request(self, request, view):
			ip = request.META.get('REMOTE_ADDR')
			import time
			ctime = time.time() # 取出当前时间
			if ip not in self.VISIT_RECORD:
				self.VISIT_RECORD[ip] = [ctime, ]
				return True
			self.history = self.VISIT_RECORD.get(ip) 
			while self.history and -ctime + self.history[-1] < 60:	#循环判断当前ip的时间列表,有值,并且当前时间减去列表的最后一个时间大于60s,把这种数据pop掉,这样列表中只有60s以内的访问时间,循环结束后,剩下的都是1分钟以后访问的时间
				self.history.pop()
			if len(self.history) < 3:	# 当列表小于3,说明一分钟以内访问不足三次,插入到列表第一个位置,返回True;当大于等于3,说明一分钟内访问超过三次,返回False验证失败
				self.history.insert(0, ctime)
				return True
			else:
				return False

		def wait(self):
			import time
			ctime = time.time()
			return 60 - (ctime - self.history[-1])

内置认证类,权限类,频率类

内置认证类:		# 来自rest_framework.authentication
	1.BasicAuthentication 	# 基础认证类
	2.RemoteUserAuthentication
	3.SessionAuthentication		# session认证
	4.TokenAuthentication		# jwt认证
	'---都建议自己写---'

内置权限类:		# 来自rest_framework.persmissions
	1.BasePermission	# 基础权限类
	2.AllowAny:允许所有用户通过,等于没有
	3.IsAuthenticated:是否登录,登录了才有权限(应用背景:认证类不抛异常)
	4.IsAdminUser:是否是管理员,管理员才有权限

内置频率类:		# 来自rest_framework.throttling
	1.BaseThrottle		# 基础频率类
	2.SimpleRateThrottle	# 常用
	3.AnonRateThrottle:用ip做用户限制,只需要配置settings.py
		例子:
			'DEFAULT_THROTTLE_RATES': {'anon': '3/m',}
	4.UserRateThrottle:用id做用户限制,只需要配置settings.py
		例子:
			'DEFAULT_THROTTLE_RATES': {'user': '3/m',}

鸭子类型

写实解释:走路像鸭子,说话像鸭子,它就是鸭子
python解释:面向对象中,子类不需要显示的继承某个类,只要这个类有某个类的相同方法和属性,那我就属于这个类
java解释:多态

问题:python鸭子类型方法容易写错
解决办法:
	1.用abc模块,装饰后,必须重写方法,不重写就报错
	2.父类中写这个方法,但没有具体实现,直接抛异常	# 例子:drf源码中使用

战略补充(模型类choices参数+request补充+settings文件导包)

模型类choices参数:		# IntegerField方法
	功能:元组套元组数据类型,将字段数据作为元组的位序实现选择功能
	例子:uer_type=models.IntegerField(choices=((1,超级用户),(2,贵宾用户),(3,普通用户))

request补充:
	get_字段名_display方法:
		前提:当前字段参数用有choices属性
		功能:实现choices属性元组数据的映射

settings.py文件导包问题:
	不要再settings.py文件中导包,会报错,因为加载顺序问题
posted @ 2023-04-25 17:27  维生素Z  阅读(36)  评论(0)    收藏  举报