三大认证之权限与频率

三大认证之权限与频率

权限类使用

我们既然通过了登录校验那么就会继续走接下来的三大认证之一:权限,也就是说我们有一些接口只有管理员才能访问,普通用户访问

# 首先导入BasePermission然后写一个类继承BasePermission
from rest_framework.permissions import BasePermission
class RightPermission(BasePermission):
    # message逐级查找message如果下面没有就会使用类中的message
    message = '啊你不是vip请充值后访问'
    # 重写has_permission方法
    def has_permission(self, request, view):
        print(request.user.user_type.name)
        if request.user.user_type.name == '超级管理员':  # 验证是否为超级管理员
            return True  # 如果在用户类型表中是超级管理员那么有资格访问,返回True
        else:  # 否则返回False
            #我们也可以给他返回自定义提示信息,如果函数中没有则去类中寻找
            # 如果我们使用的是choice的情况下,可以使用user.user_type 拿到的数字类型,想变成字符串的形式那么我们可以使用user.get_user_type_display()
            self.message = '您是: %s ,您没有权限访问该程序'%request.user.user_type.name
            return False
        # 最后去使用

频率类使用

# 首先我们导入频率类的配置,写一个类用来继承,一共有两种继承方式,一种是BaseThrottle
# 第二种则是SimpleRateThrottle,第一种需要改写allow_request,而第二种只需要改写get_cache_key
# 我们暂时先使用SimpleRateThrottle
from rest_framework.throttling import BaseThrottle,SimpleRateThrottle
# 我们继承SimpleRateThrottle
class Frequency(SimpleRateThrottle):
    # 类属性,这个类属性我们可以随便进行命名,但是需要跟配置文件相对应
    scope = 'pig'
    # 改写get_cache_key
    def get_cache_key(self, request, view):
        # 返回什么频率就会以什么做限制
        # 返回ip地址来做限制
        return request.META.get('REMOTE_ADDR')

# 写入全局配置
REST_FRAMEWORK={
    # 给全局使用,我们可以使用局部禁用来禁止频率
    'DEFAULT_THROTTLE_CLASSES':['app01.frequency.Frequency'],
    # 定义访问频率
    'DEFAULT_THROTTLE_RATES':{
        'pig':'5/m'  # 每分钟只能访问5次, 5/h 每小时只能访问5次, 5/s 每秒只能访问5次, 5/d 每天只能访问5次
    }
}

请求头HttpResponse.META

一个标准的Python 字典,包含所有的HTTP 首部。具体的头部信息取决于客户端和服务器,下面是一些示例:
  取值:
    CONTENT_LENGTH —— 请求的正文的长度(是一个字符串)。
    CONTENT_TYPE —— 请求的正文的MIME 类型。
    HTTP_ACCEPT —— 响应可接收的Content-Type。
    HTTP_ACCEPT_ENCODING —— 响应可接收的编码。
    HTTP_ACCEPT_LANGUAGE —— 响应可接收的语言。
    HTTP_HOST —— 客服端发送的HTTP Host 头部。
    HTTP_REFERER —— Referring 页面。
    HTTP_USER_AGENT —— 客户端的user-agent 字符串。
    QUERY_STRING —— 单个字符串形式的查询字符串(未解析过的形式)。
    REMOTE_ADDR —— 客户端的IP 地址。
    REMOTE_HOST —— 客户端的主机名。
    REMOTE_USER —— 服务器认证后的用户。
    REQUEST_METHOD —— 一个字符串,例如"GET" 或"POST"。
    SERVER_NAME —— 服务器的主机名。
    SERVER_PORT —— 服务器的端口(是一个字符串)。
   从上面可以看到,除 CONTENT_LENGTH 和 CONTENT_TYPE 之外,请求中的任何 HTTP 首部转换为 META 的键时,
    都会将所有字母大写并将连接符替换为下划线最后加上 HTTP_  前缀。
    所以,一个叫做 X-Bender 的头部将转换成 META 中的 HTTP_X_BENDER 键。

认证源码分析

首先我们因为都是继承了APIVIEW的执行流程,并且包装了新的request对象,并且执行了三大认证,执行视图类的方法,处理了全局异常
首先我们在APIView中找到dispatch,进入
def dispatch(self, request, *args, **kwargs):  # 
    request = self.initialize_request(request, *args, **kwargs)

在initial的内部有三个功能,也就是我们的三大认证:认证,权限,频率,我们需要该写的也在这三个内部
def initial(self, request, *args, **kwargs):
    self.perform_authentication(request)  # 认证
    self.check_permissions(request)  # 权限
    self.check_throttles(request)  # 频率

认证源码,就一句话返回了request.user 这里的request是新的request,User属性则是在Request类中,所以我们应该到Request中取寻找
    def perform_authentication(self, request):
        request.user

来到Request中寻找
@property  # 将user属性伪装成一个方法
def user(self):  
    if not hasattr(self, '_user'):  # Request类的对象中反射的_user
        with wrap_attributeerrors():
            self._authenticate()  # 如果是第一次的话那么就一定会走这个代码
    return self._user

我们顺着_authenticate寻找
    def _authenticate(self):
        for authenticator in self.authenticators:
            try:
                # (user_token.user,token)
                user_auth_tuple = authenticator.authenticate(self)  # 调用认证类对象的authenticator
            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._not_authenticated()
    # 认证类,要重写authenticate方法,我们认证后通过返回两个值或翻译一个None,那么认证不通过就抛AuthenticationFailed(继承了APIException)异常

权限源码分析

首先我们因为都是继承了APIVIEW的执行流程,并且包装了新的request对象,并且执行了三大认证,执行视图类的方法,处理了全局异常
首先我们在APIView中找到dispatch,进入
def dispatch(self, request, *args, **kwargs):  # 
    request = self.initialize_request(request, *args, **kwargs)

在initial的内部有三个功能,也就是我们的三大认证:认证,权限,频率,我们需要该写的也在这三个内部
def initial(self, request, *args, **kwargs):
    self.perform_authentication(request)  # 认证
    self.check_permissions(request)  # 权限
    self.check_throttles(request)  # 频率
    
权限源码
def check_permissions(self, request):
    for permission in self.get_permissions():
        # permission是配置在视图类中得权限类的对象,对象调用它的绑定方法has_permission,对象调用自己的绑定方法会把自己传入(权限类的对象,request,视图类的对象)
        if not permission.has_permission(request, self):
            self.permission_denied(
                request,
                message=getattr(permission, 'message', None),
                code=getattr(permission, 'code', None)
            )

进入self.get_permissions
def get_permissions(self):
    return [permission() for permission in self.permission_classes]
	 self.permission_classes  视图类中配的权限类列表
    permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES  返回的是在视图类中配的权限类的对象列表[UserTypePermession(),]

频率源码分析

首先我们因为都是继承了APIVIEW的执行流程,并且包装了新的request对象,并且执行了三大认证,执行视图类的方法,处理了全局异常
首先我们在APIView中找到dispatch,进入
def dispatch(self, request, *args, **kwargs):  # 
    request = self.initialize_request(request, *args, **kwargs)

在initial的内部有三个功能,也就是我们的三大认证:认证,权限,频率,我们需要该写的也在这三个内部
def initial(self, request, *args, **kwargs):
    self.perform_authentication(request)  # 认证
    self.check_permissions(request)  # 权限
    self.check_throttles(request)  # 频率
    
权限源码
    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(到达频率限制峰值)

鸭子类型

我们之前学的鸭子类型其实就是一句跟垃圾的话,就是走路像鸭子,说话像鸭子,那么他就是只鸭子,太过笼统和片面,硬说的话也不算错,但是并不不完满

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

# 假设有个鸭子类Duck类,有两个方法,run,speak方法
# 假设又有一个普通鸭子类,PDuck,如果它也是鸭子,它需要继承Duck类,只要继承了鸭子类,什么都不需要写,普通鸭子类的对象就是鸭子这种类型;如果不继承,普通鸭子类的对象就不是鸭子这种类型
#假设又有一个唐老鸭子类,TDuck,如果它也是鸭子,它需要继承Duck类,只要继承了鸭子类,什么都不需要写,唐老鸭子类的对象就是鸭子这种类型;如果不继承,唐老鸭子类的对象就不是鸭子这种类型

# python不推崇这个,它推崇鸭子类型,指的是
不需要显示的继承某个类,只要我的类中有run和speak方法,我就是鸭子这个类

# 有小问题:如果使用python鸭子类型的写法,如果方法写错了,它就不是这个类型了,会有问题
# python为了解决这个问题:
	-方式一:abc模块,装饰后,必须重写方法,不重写就报错
    -方式二:drf源码中使用的:父类中写这个方法,但没有具体实现,直接抛异常

练习1

perims.py

from rest_framework.permissions import BasePermission
class RightPermission(BasePermission):
    message = '啊你不是vip请充值后访问'
    def has_permission(self, request, view):
        print(request.user.user_type.name)
        if request.user.user_type.name == '超级管理员': 
            return True 
        else: 
            self.message = '您是: %s ,您没有权限访问该程序'%request.user.user_type.name
            return False

views.py

class BookView1(GenericAPIView, ListModelMixin):
    authentication_classes = [LoginAuth, ]
    permission_classes = [RightPermission, ]
    throttle_classes = [Frequency,]
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

class BookView2(GenericAPIView, CreateModelMixin):
    authentication_classes = [LoginAuth, ]
    permission_classes = [RightPermission, ]
    throttle_classes = [Frequency,]
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)


class BookDetailView1(GenericAPIView, RetrieveModelMixin):
    authentication_classes = [LoginAuth, ]
    permission_classes = [RightPermission, ]
    throttle_classes = [Frequency, ]
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    def get(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)

class BookDetailView2(GenericAPIView, UpdateModelMixin):
    authentication_classes = [LoginAuth, ]
    permission_classes = [RightPermission, ]
    throttle_classes = [Frequency, ]
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    def put(self, request, *args, **kwargs):
        return self.update(request, *args, **kwargs)


class BookDetailView(GenericAPIView, DestroyModelMixin):
    authentication_classes = [LoginAuth, ]
    permission_classes = [RightPermission, ]
    throttle_classes = [Frequency, ]
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)



class PublishView1(GenericAPIView, ListModelMixin):
    # authentication_classes = []
    permission_classes = []
    throttle_classes = [Frequency, ]
    queryset = Publish.objects.all()
    serializer_class = PublishSerializer

    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

class PublishView2(GenericAPIView, ListModelMixin, CreateModelMixin):
    # authentication_classes = []
    permission_classes = []
    throttle_classes = [Frequency, ]
    queryset = Publish.objects.all()
    serializer_class = PublishSerializer

    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)




class PublishDetailView1(GenericAPIView,  UpdateModelMixin):
    # authentication_classes = []
    permission_classes = []
    throttle_classes = [Frequency, ]
    queryset = Publish.objects.all()
    serializer_class = PublishSerializer


    def put(self, request, *args, **kwargs):
        return self.update(request, *args, **kwargs)



class PublishDetailView2(GenericAPIView, RetrieveModelMixin):
    # authentication_classes = []
    permission_classes = []
    throttle_classes = [Frequency, ]
    queryset = Publish.objects.all()
    serializer_class = PublishSerializer

    def get(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)

class PublishDetailView(GenericAPIView, DestroyModelMixin):
    # authentication_classes = []
    permission_classes = []
    throttle_classes = [Frequency, ]
    queryset = Publish.objects.all()
    serializer_class = PublishSerializer
    def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)

settings.py

REST_FRAMEWORK={
    'DEFAULT_THROTTLE_CLASSES':['app01.frequency.Frequency'],
    'DEFAULT_THROTTLE_RATES':{
        'pig':'5/m'  # 每分钟只能访问5次, 5/h 每小时只能访问5次, 5/s 每秒只能访问5次, 5/d 每天只能访问5次
    }
}

练习2

from rest_framework.permissions import BasePermission
class RightPermission(BasePermission):
    message = '啊你不是vip请充值后访问'
    # 重写has_permission方法
    def has_permission(self, request, view):
        print(request.user.user_type.name)
        if request.user.user_type.name == '超级管理员': 
            return True  
        else:  
            self.message = '您是: %s ,您没有权限访问该程序'%request.user.user_type.name
            return False

练习3

1.认证:
   1.入口APIView中的dispatch方法中执行了三大认证
   2.执行perform_authentication方法
   3.执行request.user:user方法(通过@property装饰器伪装了属性)第一次走_authenticate方法
   4.执行self._authenticate()
    for循环self.authenticators-配置在视图类中的所有认证对象
    (authenticators=None,是在新的Request中传入的参数)
    调用认证类的对象
    将返回的两个值解压赋值self.user, self.auth = user_auth_tuple
   5.所以写认证类继承BaseAuthentication需要重写authenticate方法
   6.拿到用户提交的有带认证的堆积字符串与数据库数据判断后 返回对应数据
     成功会返回两个值(token_user.user, token) 之后就可以使用登录用户对象数据
posted @ 2022-10-09 21:16  Joseph-bright  阅读(87)  评论(0编辑  收藏  举报