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

drf之权限类与频率类

时间:2022-10-09 18:45:30浏览次数:48  
标签:permission self request 认证 user 频率 权限 class drf


一、权限类

# 权限就是我们在登入之后我们在访问这个接口的时候有没有访问的权限

# eg:
    现在我们把用户等级分为两个等级,一个为vip用户,一个为普通用户
    
    然后现在出版社接口 只能允许vip用户访问,普通用户不能够访问

# 使用步骤
    -1 自己写一个类,然后继承BasePermission
    -2 然后在该类中重写has_permission方法
    -3 在该方法校验用户是否有权限(reuqest.user就是当前用户)
    -4 如果没有权限返回False,如果有权限返回True
    -5 self.message 是前端看到的信息

    -6 使用又三种 跟认证一样 全局配置,局部使用,局部禁用

1.代码演示

我们可以在app下新建一个permission.py文件, 在该文件下写权限类

from rest_framework.permissions import BasePermission
class UserTypePermission(BasePermission):
    def has_permission(self, request, view):
        if request.user.user_type == 1:
            return True  # 通过权限就返回True即可
        else:
            self.message = '您不是vip用户不能访问'  # 这个是返回个前端看到的信息,因为名字的查找顺序,所以会直接使用对象本身的message所以返回的是当前语句
           # 还可以这样写
            self.message = '您是%s,不能访问' % request.user.get_user_type_display()
            return False
        pass

view.py

class BooksView(ModelViewSet):
    authentication_classes = [LoginAuth]  # 因为权限是登入用户才会有的 所以要先判断用户是否登入先
    permission_classes = [UserTypePermission,]  # 跟认证类一样写一个属性在列表中写权限类
    queryset = Book.objects.all()
    serializer_class = BookSerializer

model.py

class User(models.Model):
    username = models.CharField(max_length=32)
    password = models.CharField(max_length=32)
    user_type = models.IntegerField(choices=((1, 'vip用户'), (2, '普通用户')), default=1)  # 因为要有个字段来表名用户是什么用户所以加了一个字段 然后使用了choice

配置

# 全局配置
REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': ['app01.permission.UserTypePermission'],
}

# 局部配置
class BooksView(ModelViewSet):
    authentication_classes = [LoginAuth]
    permission_classes = [UserTypePermission,]
    queryset = Book.objects.all()
    serializer_class = BookSerializer

# 局部禁用
class BooksView(ModelViewSet):
    authentication_classes = [LoginAuth]
    permission_classes = []  # 空的即可
    queryset = Book.objects.all()
    serializer_class = BookSerializer

 

 

 二、频率类

# 就是一个用户是否登入 都要限制访问的频率 不能一直访问 比如一分钟只能访问3次


# 使用步骤
    -1 写一个类,然后继承SimpleRateThrottle
    -2 然后重写get_cache_key, 然后返回一个唯一的字符串,会以这个字符串做频率限制
    -3 写一个类属性 scope='随便写'  #但是要跟配置文件对应
    -4 配置文件中写
    REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_RATES': {
        'luffy': '3/m'  # 每分钟只能访问3次,还可以写 '3/h   3/d   3/s'
    },
}
    -5 也是有全局配置,局部使用,局部禁用

代码演示

在app下新建一个throttling.py 在该文件下写频率类

from rest_framework.throttling import SimpleRateThrottle

class MyThrottling(SimpleRateThrottle):
    scope = 'luffy'    # 这里的名字要跟配置文件中一样

    def get_cache_key(self, request, view):
        # 返回什么就以什么做限制
        # 可以是用户ip,用户id
        return request.META.get('REMOTE_ADDR')  # 以用户ip做限制

settings.py

REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_RATES': {
        'luffy': '3/m'   # 前面的K要跟频率类中scope的名称一样
    }
}

view.py

class BooksView(ModelViewSet):
    throttle_classes = [MyThrottling, ]  # 因为这个不需要用户登入也要限制频率 所以不需要校验用户登入也可以
    queryset = Book.objects.all()
    serializer_class = BookSerializer

配置

# 全局配置
REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_CLASSES': ['app01.throttlings.MyThrottling'],
    'DEFAULT_THROTTLE_RATES': {
        'luffy': '3/m'
    }
}

# 局部使用
class BooksView(ModelViewSet):
    throttle_classes = [MyThrottling, ]  # 如果全局配置为话这个就可以不用再写了
    queryset = Book.objects.all()
    serializer_class = BookSerializer

# 局部禁用
class BooksView(ModelViewSet):
    throttle_classes = []  # 局部禁用写个空列表即可
    queryset = Book.objects.all()
    serializer_class = BookSerializer

 

 

 三、认证类源码分析

# 写个认证类,重写某个方法,配置在视图类上,就有认证了---》认证类加了,在视图类的方法中,request.user就是当前登录用户---》猜认证类的执行,是在在视图类的方法之前执行的
# 之前我们读APIView的源码的时候 知道了APIView进行了一下操作:
    -把request包装了一下 变成了新的request了
    -执行三大认证
    -执行视图类
    -全局异常捕获

# 所以查看认证类的源码的入口就是从执行三大认证开始查看
    '''
    首先在APIView的dispatch中写了三大认证
     self.initial(request, *args, **kwargs)
     然后我们开始从最开始的类中找initial的方法 我们会发现最后又找到了APIView中
    所以我们查看APIView中的initial方法'''

def initial(self, request, *args, **kwargs):
    self.perform_authentication(request)
    self.check_permissions(request)
    self.check_throttles(request)
    # 最精华的就这三句  从上至下 执行了认证,权限,频率
    # 所以我们有要去最开始找perform_authentication方法 然后我们有会发现最后这个方法又在APIView中 所以我们现在看一些APIView中的perform_authentication方法

def perform_authentication(self, request):
    request.user  # 我们可以看到该方法只写一个request.user  而这个request肯定变成了新的request了 因为三大认证在包装request后面执行 所以这个request肯定是新的了

# 所以我们要去看一下Request类中user方法 因为新的request是Request类的对象

1.Request源码分析

@property  # 我们可以看到该方法被封装成了数据属性
def user(self):
    if not hasattr(self, '_user'):  
        # 通过反射判断对象中有没有_user属性  我们自己写的类肯定是没有的所以是False 然后前面有加上了not 所以是True
        with wrap_attributeerrors():  # 上下文管理
            self._authenticate()  # 所以第一次肯定走这个
    return self._user  # if完之后 返回了self._user 在我们自己写的类中设置了_user  所以之后都有了_user属性之后就不会在走if判断了

# 所以现在我们要去看一下_authenticate方法  然后通过查找顺序可以知道最后又在Request中找到了这个方法 

    def _authenticate(self):
    for authenticator in self.authenticators:  # 这个循环的就是我们在视图类中配置的认证类对象
        try:
            user_auth_tuple = authenticator.authenticate(self)  # 因为需要调用了authenticate所以我们需要重写authenticate方法
            # user_auth_tuple  就是等于 认证通过的时候我们返回的两个值  (user_token.user, token)
            # 这里我们自己写的认证类中的authenticate方法传了两个值为什么这里可以值传一个值呢?
            # 因为自己写的类中的authenticate第一个值是self所以对象调用该方法的时候会把自己当做第一个参数传入所以没事
            # 然后又把Request的对象传入 所以authenticate第二个参数还是request
        except exceptions.APIException:  # 没有认证通过就会抛异常并处理
            self._not_authenticated()
            raise
        if user_auth_tuple is not None:
            self._authenticator = authenticator
            self.user, self.auth = user_auth_tuple  
            # 这里就是解压赋值将(user_token.user, token)赋值给了 self.user, self.auth
            return
            # 认证类可以是多个,但是只要有一个返回了两个值,那么就会直接结束 因为这里直接return了
            # 所以我们在写多个认证类的时候 只需要在最后一个认证类返回两个值即可 前面的返回None
    self._not_authenticated()

self.authenticators为什么就是我们在视图类中配置的认证类对象

class Request:
    def __init__(self, request, parsers=None, authenticators=None,
                 negotiator=None, parser_context=None):
        self.authenticators = authenticators or ()
        # 我们可以看到Request在实例化对象的时候 其实我们把认证类给传进去了

# 然后我们想到Request实例化对象是在APIView中dispatch中实例化对象操作
    def dispatch(self, request, *args, **kwargs):
        request = self.initialize_request(request, *args, **kwargs)

# 所以我们可以看一下initialize_request这个是怎么写的
    def initialize_request(self, request, *args, **kwargs):
        return Request(
            request,
            parsers=self.get_parsers(),
            authenticators=self.get_authenticators(),
            negotiator=self.get_content_negotiator(),
            parser_context=parser_context
        )
    # 这个就是返回了Request实例化对象 然后实例化对象的时候还是有传入get_authenticators

# 我们可以看一写get_authenticators写了啥
    def get_authenticators(self):
        return [auth() for auth in self.authentication_classes]
            # 就是返回了一个列表生成式  而这个authentication_classes 就是我们在视图类中配置的认证类对象

认证类源码分析总结

#  总结:认证类,要重写authenticate方法,认证通过返回两个值或None,认证不通过抛AuthenticationFailed(继承了APIException)异常

四、权限类源码分析

# 权限类的入口也是一样的是在三大认证中
    def initial(self, request, *args, **kwargs):
        self.perform_authentication(request)
        self.check_permissions(request)
        self.check_throttles(request)
        # 最精华的就这三句  从上至下 执行了认证,权限,频率

# 所以我们要从头开始找check_permissions这个方法在哪个类中,找到最后我们会发现这个方法还是在APIView中所以我们只需要在该类中找到这个方法看看这个方法写了啥
        def check_permissions(self, request):
        for permission in self.get_permissions():  # 这个循环的就是我们写在视图类中的权限对象
            if not permission.has_permission(request, self):  # 然后调用has_permission方法所以我们需要在自己写的类中重写has_permission方法
            # 然后这个是一个if判断所以我们只需要返回True和False即可  这里也是一样为什么我们自己写的类写了三个参数而这里只需要传两个参数
            # 因为在对象调用has_permission方法的时候会把自己当做第一个参数传入,然后第二个参数self因为这个方法在APIView中所以self就是APIView的对象
            # 所以自己写的类中的has_permission的第三个参数就是view
            # 然后当我们返回False的时候认证不通过的时候 就会执行下面的代码
                self.permission_denied(  
                    request,
                    message=getattr(permission, 'message', None),  
                    # 这个就是通过反射获取message参数 所以我们可以在自己写的类中写message属性
                    code=getattr(permission, 'code', None)
                    # 这个就是为了规范需要返回{code:101}
                )

 五、鸭子类型

# 鸭子类型就是 在面向对象中 子类不需要显示继承了某个类,只要有某个类的方法和属性,那么就属于这个类

eg:
    # 假设现在有一个Duck类,该类中有run方法和speak方法
    # 假设现在有一个普通鸭类,PDuck,如果它也是鸭子,那么就继承Duck类,那么里面什么都不需要写,普通鸭子类的对象就是鸭子这种类型,如果不继承,那么普通鸭子类型的对象就不是鸭子这种类型
    # 假设现在又有一个唐老鸭类,TDuck,如果它也是鸭子,那么就继承Duck类,那么里面什么都不需要写,唐老鸭类的对象就是鸭子这种类型,如果不继承,那么唐老鸭类型的对象就不是鸭子这种类型

# 但是python不推崇这个,推崇的是鸭子类型,指的是
    -就是我们在子类不要显示继承的某个类,只需要我的类中有run和speck方法那么我就是鸭子这个类

# 但是这个有一个小问题 那就是我们在编写子类的时候 如果不写父类的话 那么我们如果把父类的方法名写错的话 那么我们这个子类就不是父类这个类型了
# python为了解决这问题
    -方式1:abc模块,装饰后必须重写方法,不重写就会报错
    -方式2:父类中的方法不写东西直接报错,那么子类必须重写这个方法

 

标签:permission,self,request,认证,user,频率,权限,class,drf
From: https://www.cnblogs.com/stephenwzh/p/16772915.html

相关文章