drf-认证、权限、频率组件

目录

-认证组件

--认证简介

--使用方法

--源码分析

--使用

---局部使用

---全局使用

---局部禁用

-权限组件

--使用方法

--源码分析

--使用

---局部使用

---全局使用

---局部禁用

--内置权限校验

-频率组件

--内置频率类校验使用

---局部使用

---全局使用

--自定义频率


-认证组件

--认证简介

只有认证通过的用户才能访问指定的url地址,比如:查询课程信息,需要登录之后才能查看,没有登录,就不能查看,这时候需要用到认证组件

--使用方法

在app下新建一个.py文件,在其中写一个认证类(必须继承BaseAuthentication),在该类中重写authenticate方法,认证逻辑都写在其中。若认证通过,返回两个值;认证失败,抛出异常APIException或AuthenticationFailed(继承的是APIException,一般都用它)。

--源码分析

APIView-->dispatch()方法-->request = self.initialize_request(request, *args, **kwargs)-->
initialize_request()返回Request()对象,里面的authenticators从get_authenticators()中得到-->
get_authenticators(),返回return [auth() for auth in self.authentication_classes],列表生成
式,从配置的authentication_classes中一个个取类加括号生成对象-->authentication_classes在APIView
类最初配置的authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES-->搞清楚
authenticators从哪来的后-->再看dispatch方法中的关键-->self.initial(request, *args, **kwargs)
-->initial()中有三大组件——认证、权限、频率:
	# 三大组件-认证 校验用户:游客、合法用户、非法用户
	# 游客:代表校验通过,直接进入下一步校验(权限校验)
	# 合法用户:代表校验通过,将用户存在request.user中,再进入下一步校验(权限校验)
	# 非法用户:代表校验失败,抛出异常,返回403forbidden
	self.perform_authentication(request)
	# 三大组件-权限 校验用户权限:必须登录、所有用户、登录读写游客只读、自定义用户角色
	# 认证通过:可以进入下一步校验
	# 认证失败:抛出异常403
	self.check_permissions(request)
	# 三大组件-频率 限制视图接口被访问的频率数:限制条件(ip,id)、频率周期时间(s,m,h)、频率次数(几次/s)
	# 没有达到限次:正常访问接口
	# 达到限次:限制时间内不能访问,限制时间后可以访问
    self.check_throttles(request)
-->先看认证perform_authentication()-->只有一句话request.user-->此user来自drf的Request,应去
drf的Request类中找user属性/方法-->点击Ctrl+user去drf的Request-->self._authenticate()

核心

def _authenticate(self):
    for authenticator in self.authenticators:
        # authenticators是可迭代对象,是一个列表,里面是配置的认证类加括号产生的对象
        # 拿到一个个认证器,进行认证
        try:
            # 认证器调用认证方法authenticate(自己的认证类重写的)
            # 该方法应返回一个包含user,auth的元组
            # 该方法被try包裹,代表该方法会抛异常,抛异常即为认证失败
            user_auth_tuple = authenticator.authenticate(self) #self是request对象的
            except exceptions.APIException:
            self._not_authenticated()
            raise
        # 返回值的处理
        if user_auth_tuple is not None:
            self._authenticator = authenticator
            # 如果有返回值,就将登录用户 与 登录认证 解压赋值到request.user request.auth中
            self.user, self.auth = user_auth_tuple
            return
    # 如果返回值user_auth_tuple为空,代表认证通过,但是没有登录用户与登录认证信息,表名这是游客
    self._not_authenticated()

--使用

models.py

'''首先新建一个用户表,用于校验登录信息。这自己写了个token,并单独建了一个和User一对一关联的UserToken表'''
class User(models.Model):
	username = models.CharField(max_length=32)
	password = models.CharField(max_length=32)
	user_type = models.IntegerField(choices=((1,'超级用户'), (2,'普通用户'), (3,'游客')))
class UserToken(models.Model):
	token = models.CharField(max_length=64)
    user = models.OneToOneField(to='User', on_delete=models.CASCADE)

auth_user.py

'''我在app下新建了user_auth.py文件,里面写认证类'''
from rest_framework.authentication import BaseAuthentication
from app01 import models
from rest_framework.exceptions import AuthenticationFailed

class MyAuthentication1(BaseAuthentication):
	# 重写authenticate方法
	def authenticate(self, request):
	    token = request.GET.get('token') # token一般放在浏览器中,这为了演示方便把它放在url?后
	    if token:
	        user_token_obj = models.UserToken.objects.filter(token=token).first()
	        if user_token_obj:
	            # 认证通过 返回两个值
	            return user_token_obj.user, token
	        else:
	            # 认证失败 抛异常
	            raise AuthenticationFailed('认证失败')
	    else:
            raise AuthenticationFailed('请求中未携带token认证信息!')

views.py

# 登录视图类
from rest_framework.views import APIView
from rest_framework.response import Response
from app01 import models
import uuid
class LoginAPIView(APIView):
	# 登录发的是post请求
	def post(self, request):
	    username = request.data.get('username')
	    password = request.data.get('password')
	    user_obj = models.User.objects.filter(username=username,password=password).first()
	    if user_obj:
	        # 登录成功, 生成一个随机字符串
	        token = uuid.uuid4() # 生成随机token,利用uuid模块,几乎不会重复
	        # 将token存到UserToken表中
	        # 此方法每次登录都会存一个token,既耗表资源也不恰当
	        # models.UserToken.objects.create(token=token, user=user_obj)
	        # 我们需要的是一个用户就一行表资源,每次登录将token更新即可,
	        # 而一次也未登录的应该生成token存在UserToken表中
	        models.UserToken.objects.update_or_create(defaults={'token':token}, user=user_obj)
	        # update_or_create(self, defaults=None, **kwargs)使用给定的kwargs查找一个对象,如果对象存在,则用默认值更新它,否则创建一个新对象。
	        return Response({'status':100,'msg':'登录成功','token':token})
	    else:
            return Response({'status':101,'msg':'用户名或密码错误'})

认证类使用顺序:先用视图类中的验证类,再用settings里配置的验证类,最后用默认的验证类 

---局部使用

局部使用,只需要在视图类里加入:authentication_classes = [MyAuthentication1, ]

from rest_framework.viewsets import ModelViewSet
from app01.user_auth import MyAuthentication1
class PublishModelViewSet(ModelViewSet):
    authentication_classes = [MyAuthentication1]
    queryset = models.Publish.objects
    serializer_class = PublishSerializers
'''
此时get请求访问所有:
    http: //127.0.0.1:8000/publishes/?token=12a3bb54-fe89-4451-ae1a-b3891659b523
'''

---全局使用

'''所有视图类都认证,去setting.py中配置:'''
REST_FRAMEWORK = {
	'DEFAULT_AUTHENTICATION_CLASSES':['app01.user_auth.MyAuthentication1',]
} # 是列表,可多个

---局部禁用

'''对配了全局的又想某些视图类不用,则在该视图类中重赋值为空'''
authentication_classes = []

-权限组件

之前的auth模块里自带权限校验is_staff(),这学的是drf的我们自己写的权限校验,但其实drf也有对应方法支持之前的auth模块带的权限校验,后面会有

--使用方法

在.py文件中新建一个类,继承BasePermission,重写has_permissioin方法,如果权限通过返回True;权限不通过返回False。

--源码分析

APIView-->dispatch()方法-->self.initial(request, *args, **kwargs)-->
self.check_permissions(request)-->def check_permissions(self, request)
-----核心:
    def check_permissions(self, request):
        # 遍历权限对象列表得到一个个权限对象(权限器),进行权限认证
        for permission in self.get_permissions():
            # 权限类一定得有个has_permission方法,用来写权限认证逻辑
            # 参数:权限对象self,请求对象request,视图类对象view
            # 返回值:有权限True,无权限False
            if not permission.has_permission(request, self):
                self.permission_denied(
                    request, message=getattr(permission, 'message', None)
                )

--使用

user_permission.py

from rest_framework.permissions import BasePermission
class MyPermission1(BasePermission):
	# 重写has_permission方法
	def has_permission(self, request, view):
	    # 是超级用户才能访问指定内容
	    user_obj = request.user # 由于已经认证过了,所以request中有user
	    print(user_obj.get_user_type_display()) # 显示choices字段对应的中文信息:对象.get_字段名_display()
	    if user_obj.user_type == 1:
	        return True
	    else:
            return False

model.py沿用认证里的

urls.py

path('permit1/', views.PublishAPIViewPermissionTest1.as_view()),
path('permit2/', views.PublishAPIViewPermissionTest2.as_view()),

权限类使用顺序:先用视图类中的权限类,再用settings里配置的权限类,最后用默认的权限类 

---局部使用

from app01 import user_permission
class PublishAPIViewPermissionTest1(APIView):
	authentication_classes = [MyAuthentication1]
	permission_classes = [user_permission.MyPermission1]
	def get(self, request):
	    return Response('权限测试,只有超级用户才能看到哦~')
class PublishAPIViewPermissionTest2(APIView):
	authentication_classes = [MyAuthentication1]
	def get(self, request):
        return Response('权限测试,普通用户也能看到哦~')

---全局使用

REST_FRAMEWORK = {
	'DEFAULT_PERMISSION_CLASSES':['app01.user_permission.MyPermission1'], # 权限
} # 注意:在REST_FRAMEWORK里面配置就行了,不能命两个!!!

---局部禁用

permission_classes = []

--内置权限校验

drf内置的权限校验只能是通过admin中设置的用户,因为它用了IsAdminUser等它内置的

# 内置权限IsAdminUser演示

# 1、创建超级管理员createsuperuser
# 2、写一个测试视图类
from rest_framework.permissions import IsAdminUser
from rest_framework.authentication import SessionAuthentication
class TestView(APIView):
    authentication_classes = [SessionAuthentication, ]
    permission_classes = [IsAdminUser]
    def get(self, request):
        return Response('superuser可看')
# 3、超级用户登录admin,再访问test就有权限
# 4、正常用户没有权限看

-频率组件

频率即控制某个用户访问某个url的次数,如一分钟内只能三次等

drf内置的频率校验只能操作到admin用户或通过admin后台管理设置的用户

--内置频率类校验使用

drf自带的内置频率校验类有匿名用户类(未登录用户)AnonRateThrottle,登录用户类UserRateThrottle。

---局部使用

在视图类下配置throttle_classes = [AnonRateThrottle] # 内置匿名用户限制
在settings.py中的REST_FRAMEWORK中写'DEFAULT_THROTTLE_RATES':{'anon':'3/m'} 
    # 限制频率次数,1min(h-hour,y-year…)内只能3次,anon与AnonRateThrottle对应;user与UserRateThrottle对应

---全局使用

在settings.py中配两个参数
	REST_FRAMEWORK = {
		'DEFAULT_THROTTLE_CLASSER':['rest_framework.throttling_UserRateThrottle'],
		'DEFAULT_THROTTLE_RATES':{'user':'10/m'} # 必带
    }

--自定义频率

自定义频率校验实现逻辑如下,就是要重写allow_request()和wait()两个方法,继不继承BaseThrottle都行因为Python实行的是鸭子类型(我有鸭子的两个方法特征了,我就是鸭子了),但实现的较low,3/60写的死不动态,可查看SimpleRateThrottle类的源码学习思维

'''自定义的逻辑'''
# (1)取出访问者ip
# (2)判断当前ip不在访问字典里,添加进去,并且直接返回True,表示第一次访问,在字典里,继续往下走
# (3)循环判断当前ip的列表,有值,并且当前时间减去列表的最后一个时间大于60s,把这种数据pop掉,这样列表中只有60s以内的访问时间,
# (4)判断,当列表小于3,说明一分钟以内访问不足三次,把当前时间插入到列表第一个位置,返回True,顺利通过
# (5)当大于等于3,说明一分钟内访问超过三次,返回False验证失败
eg:
class MyThrottles():
    VISIT_RECORD = {}
    def __init__(self):
        self.history=None
    def allow_request(self,request, view):
        #(1)取出访问者ip
        # print(request.META)
        ip=request.META.get('REMOTE_ADDR')
        import time
        ctime=time.time()
        # (2)判断当前ip不在访问字典里,添加进去,并且直接返回True,表示第一次访问
        if ip not in self.VISIT_RECORD:
            self.VISIT_RECORD[ip]=[ctime,]
            return True
        self.history=self.VISIT_RECORD.get(ip)
        # (3)循环判断当前ip的列表,有值,并且当前时间减去列表的最后一个时间大于60s,把这种数据pop掉,这样列表中只有60s以内的访问时间,
        while self.history and ctime-self.history[-1]>60:
            self.history.pop()
        # (4)判断,当列表小于3,说明一分钟以内访问不足三次,把当前时间插入到列表第一个位置,返回True,顺利通过
        # (5)当大于等于3,说明一分钟内访问超过三次,返回False验证失败
        if len(self.history)<3:
            self.history.insert(0,ctime)
            return True
        else:
            return False
    def wait(self):
        import time
        ctime=time.time()
        return 60-(ctime-self.history[-1])

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

weer-wmq

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值