首页 > 其他分享 >权限类与频率类

权限类与频率类

时间:2022-10-09 22:00:07浏览次数:47  
标签:permission self request 访问 user 频率 权限

权限类使用

1.认证:校验用户是否登录
-用户登录了,但有些接口只有拥有权限的用户才能访问,其他用户不能访问
'如:出版社的所有接口,必须登录,而且是管理员才能访问'
-在用户表添加一条用户身份
# choices本质就是有关联,后期不会修改的情况下可以使用choices
user_type = models.IntegerField(default=3, choices=((1, '管理员'), (2, '用户'), (3, '游客')))

2.编写方法:
-写一个继承BasePermission的类
-重写has_permission方法
-在方法中效验用户是否有权限(request.user就是当前登录用户)
-有权限则返回True,没有权限则返回False
-self.message是给前端的提示信息

3.权限类
class UserTypePermission(BasePermission):
    def has_permission(self, request, view):
        # 只有管理员有权限
        if request.user.user_type == 1:
            return True  # 有权限
        # user.user_type拿到的是数字类型,要变成对应的字符串user.get_user_type_display()
        self.message = "你是'%s',没有权限访问" % request.user.get_user_type_display()
        return False  # 没有权限

4.在配置文件中全局配置
REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': ['app01.auth.UserTypePermission'],
}

5.局部禁用
class UserView(ViewSet):
    # 不需要登录
    authentication_classes = []
    # 不需要是超级管理员
    permission_classes = []

频率类的使用

1.无论是否有权限,都要限制访问的频率
'ru:一分钟访问3次'

2.编写方法:
-写一个继承SimpleRateThrottle的类
-重写get_cache_key方法,返回唯一的字符串,会以这个字符串做频率限制
-写一个scope = '类属性名',这个属性名要跟配置文件对象
-在配置文件中写:
REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_RATES': {
        # 一分钟访问3次
        'luffy': '3/m'
        # h时、m分、s秒、d天
    },
}

3.频率类
class MyThrottling(SimpleRateThrottle):
    # 类属性,这个类属性可以随意命名,但要和配置文件对应
    scope = 'luffy'

    def get_cache_key(self, request, view):
        # 返回什么就以什么做限制,可以通过用户ID或IP地址做限制
        return request.META.get('REMOTE_ADDR')  # 根据IP地址做限制

4.在配置文件中全局配置
REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_RATES': {
        'DEFAULT_THROTTLE_CLASSES': ['app01.auth.MyThrottling'],
        # 一分钟访问3次
        'luffy': '3/m'
        # h时、m分、s秒、d天
    },
}

5.局部禁用
class PublishView(ModelViewSet):
    # 不需要频率限制
    throttle_classes = []

源码分析

1.源码分析
-APIView包装了新的request,执行了3大认证,执行视图类的方法,处理全局异常
-所以入口就是APIView的dispatch
-在APIView的dispatch的496上下
self.initial(request, *args, **kwargs)
# 它是APIView的initial

-到initial的文件里,414行上下,分别是:认证、权限、频率
self.perform_authentication(request)
self.check_permissions(request)
self.check_throttles(request)

认证源码分析

-认证类源码在APIView的perform_authentication,在316行上下
def perform_authentication(self, request):
    request.user  # 新的request

-request是新的request,所以要到Request类中找user属性(方法),这个属性是个方法包装成数据属性
-到Request类中找,220行
@property
def user(self):
    # Request类的对象中反射_user
    if not hasattr(self, '_user'):
        with wrap_attributeerrors():
            # 第一次会走这个代码
            self._authenticate()
    return self._user

-Request的self._authenticate(),373行
def _authenticate(self):
    # authenticator是配置在视图类中认证类的对象
    for authenticator in self.authenticators:
        try:
            # 调用认证类对象的authenticate方法
            # 认证通过后返回两个值,认证不通过则抛异常
            # user_auth_tuple=(user_token.user, token)
            user_auth_tuple = authenticator.authenticate(self)
        except exceptions.APIException:
            self._not_authenticated()
            raise

        if user_auth_tuple is not None:
            self._authenticator = authenticator
            # 将user_auth_tuple解压赋值
            self.user, self.auth = user_auth_tuple
            return
        # 认证类可以配多个,但是如果有一个返回值,后续的就不执行了

    self._not_authenticated()

-所以要重写authenticator方法,认证通过返回两个值或None,认证不通过则抛异常

权限类源码分析

-权限类源码在APIView的check_permissions,在326行上下
def check_permissions(self, request):
    for permission in self.get_permissions():
        # permission是配置在视图类中权限类的对象,对象调用它的绑定方法has_permission
        # 对象调用自己的绑定方法会把自己传入(self, request, self)---->(权限类的对象, request, 视图类的对象)
        if not permission.has_permission(request, self):
            # 如果重写的has_permission返回的是False则执行下面的代码,就是没有权限,如果返回的是True则不执行下面的代码,就是有权限
            self.permission_denied(
                request,
                # 去对象中反射了message和code
                message=getattr(permission, 'message', None),
                code=getattr(permission, 'code', None)
            )

-APIVIew的self.get_permissions(),273行左右
return [permission() for permission in self.permission_classes]
-self.permission_classes就是在视图类中配的权限类的列表
-所以get_permissions返回的是视图类中配的权限类的列表
permission_classes = [UserTypePermission]

-所以要重写has_permission方法,方法中有三个参数,一定要返回True或False

部分频率类源码分析

-频率类源码在APIView的check_throttles,在352行上下
def check_throttles(self, request):
    throttle_durations = []
    for throttle in self.get_throttles():
        if not throttle.allow_request(request, self):
            throttle_durations.append(throttle.wait())

-写频率类,必须重写allow_request方法,返回True(没有频率的限制)或False(到了频率的限制)

鸭子类型

1.鸭子类型指的是面向对象中,子类不需要显示的继承某个类,只要有某个方法和属性,那么这个子类就属于这个类
-假设有个鸭子类Duck类,有两个方法run、speak方法
-假设有一个唐老鸭类TDuck类,如果它也是鸭子,它需要继承Duck类,只要继承了鸭子类,它就是鸭子类,其他的都不需要写

2.python不推崇这种,它的鸭子类型指的是不需要显示的继承某个类,只要类中有run和speak方法,就是鸭子类
-python鸭子类型的写法,如果写错了它就不是鸭子类了,python的解决方式
	-方式1:abc模块,装饰后必须重写方法,吧重写就报错
	-方式2:drf源码中使用的--》父类中写这个方法,但没有具体实现就抛异常

练习

# 1 编写图书和出版社的5个接口,所有接口都要有一分钟访问5次的频率限制
# 2 图书的接口需要登录才能方法,出版社的接口需要登录,并且是超级用户才能访问
# 3 整理认证和频率的执行流程
---------可以不需要写----
# 3 继承BaseThrottle编写频率类
	# 自定义的逻辑  {ip:[时间1,时间2]}
    #(1)取出访问者ip
    #(2)判断当前ip不在访问字典里,添加进去,并且直接返回True,表示第一次访问,在字典里,继续往下走
    #(3)循环判断当前ip的列表,有值,并且当前时间减去列表的最后一个时间大于60s,把这种数据pop掉,这样列表中只有60s以内的访问时间,
    #(4)判断,当列表小于3,说明一分钟以内访问不足三次,把当前时间插入到列表第一个位置,返回True,顺利通过
    #(5)当大于等于3,说明一分钟内访问超过三次,返回False验证失败
    
    
    class OurThrottling(BaseThrottle):
        def allow_request(self, request, view):
            # 自定义的逻辑  {ip:[时间1,时间2]}
            # (1)取出访问者ip
            # (2)判断当前ip不在访问字典里,添加进去,并且直接返回True,表示第一次访问,在字典里,继续往下走
            # (3)循环判断当前ip的列表,有值,并且当前时间减去列表的最后一个时间大于60s,把这种数据pop掉,这样列表中只有60s以内的访问时间,
            # (4)判断,当列表小于3,说明一分钟以内访问不足三次,把当前时间插入到列表第一个位置,返回True,顺利通过
            # (5)当大于等于3,说明一分钟内访问超过三次,返回False验证失败
            return False

1.2

1.频率类
from .models import UserToken
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from rest_framework.permissions import BasePermission
from rest_framework.throttling import BaseThrottle, SimpleRateThrottle


class LoginAuth(BaseAuthentication):
    def authenticate(self, request):
        token = request.META.get('HTTP_TOKEN')
        user_token = UserToken.objects.filter(token=token).first()
        if user_token:
            return user_token.user, token
        raise AuthenticationFailed('你还没登录')


class UserTypePermission(BasePermission):
    def has_permission(self, request, view):
        if request.user.user_type == 1:
            return True
        self.message = "你是'%s',没有权限访问" % request.user.get_user_type_display()
        return False
    
    
class MyThrottling(SimpleRateThrottle):
    scope = 'luffy'

    def get_cache_key(self, request, view):
        return request.method

2.视图类
class BookView(ModelViewSet):
    permission_classes = []
    serializer_class = BookSerializer
    queryset = Book.objects.all()


class PublishView(ModelViewSet):
    serializer_class = PublishSerializer
    queryset = Publish.objects.all()

标签:permission,self,request,访问,user,频率,权限
From: https://www.cnblogs.com/riuqi/p/16773843.html

相关文章