ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

drf之认证、权限、限流、过滤

2021-06-28 21:01:58  阅读:173  来源: 互联网

标签:self request 认证 authentication 限流 user 权限 def drf


目录

drf之认证、权限、限流、过滤

一 drf认证Authentication

1 drf认证功能介绍

0) 认证,频率,权限
1) 用户是否登录到系统中
2) 后期基本上会用JWT的认证
3) 自定制的认证

2 认证功能源码分析

2.1 源码分析

1) APIView---》dispatch---》self.initial(request, *args, **kwargs)--》self.perform_authentication(request)---》Request.user--->self._authenticate(self):Request类的方法---》self.authenticators:Request类的属性---》在Request对象实例化的时候传入的----》Request在什么时候实例化的?dispatch的时候---》APIView:self.get_authenticators()--》return [auth() for auth in self.authentication_classes]----》如果在自己定义的视图类中写了authentication_classes=[类1,类2]----》Request的self.authenticators就变成了我们配置的一个个类的对象

# # 源码分析详解
# a) APIView---》
    @classmethod
    def as_view(cls, **initkwargs): 
        view = super().as_view(**initkwargs)

	# 调用view下的as_view()
    @classonlymethod
    def as_view(cls, **initkwargs):        
        def view(request, *args, **kwargs):
            self = cls(**initkwargs)
            if hasattr(self, 'get') and not hasattr(self, 'head'):
                self.head = self.get
            self.request = request
            self.args = args
            self.kwargs = kwargs
            return self.dispatch(request, *args, **kwargs)

# b) dispatch---》  # 调用了dispatch,APIView中有此方法,故是APIView中的dispatch方法
    def dispatch(self, request, *args, **kwargs):
        """
        `.dispatch()` is pretty much the same as Django's regular dispatch, but with extra hooks for startup, finalize, and exception handling.        """
        self.args = args
        self.kwargs = kwargs
        request = self.initialize_request(request, *args, **kwargs)
        self.request = request   # 对request进行了属性的赋值

        try:
            self.initial(request, *args, **kwargs)   #  运行位置

# c) self.initial(request, *args, **kwargs)--》
    def initial(self, request, *args, **kwargs):        
        self.perform_authentication(request)  # 运行到此处
        self.check_permissions(request)
        self.check_throttles(request)

# d) self.perform_authentication(request)---》  # 运行
    def perform_authentication(self, request):
        """
        Perform authentication on the incoming request. Note that if you override this and simply 'pass', then authentication will instead be performed lazily, the first time either
 `request.user` or `request.auth` is accessed.        """
        # 不是原生django的user,是新的Request对象的方法
        request.user

# e) Request.user--->  # Request下的user方法
	@property
    def user(self):
        """  Returns the user associated with the current request, as authenticated by the authentication classes provided to the request.   """
        if not hasattr(self, '_user'):
            with wrap_attributeerrors():
                self._authenticate()
        return self._user

# f) self._authenticate(self):Request类的方法---》
    def _authenticate(self):
        """ Attempt to authenticate the request using each authentication instance in turn. """
        for authenticator in self.authenticators:  # 是Request类的属性
            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

        self._not_authenticated()

# g) self.authenticators:  Request类的属性---》  h) 在Request对象实例化的时候传入的----》
    def __init__(self, request, parsers=None, authenticators=None, negotiator=None, parser_context=None):
        assert isinstance(request, HttpRequest), (
            'The `request` argument must be an instance of '
            '`django.http.HttpRequest`, not `{}.{}`.'
            .format(request.__class__.__module__, request.__class__.__name__)
        )
        self._request = request
        self.parsers = parsers or ()
        self.authenticators = authenticators or ()

# i) Request在什么时候实例化的?  dispatch开始的时候的时候---》
    def dispatch(self, request, *args, **kwargs):
        """
        `.dispatch()` is pretty much the same as Django's regular dispatch, but with extra hooks for startup, finalize, and exception handling.        """
        self.args = args
        self.kwargs = kwargs
        request = self.initialize_request(request, *args, **kwargs)  # request在APIView的dispatch这里实例化的

# j) APIView:self.get_authenticators()--》
    def initialize_request(self, request, *args, **kwargs):
        """       Returns the initial request object.        """
        parser_context = self.get_parser_context(request)

        return Request(
            request,
            parsers=self.get_parsers(),
            authenticators=self.get_authenticators(),
            negotiator=self.get_content_negotiator(),
            parser_context=parser_context
        )

# k) return [auth() for auth in self.authentication_classes]----》
    def get_authenticators(self):
        """        Instantiates and returns the list of authenticators that this view can use.        """
        return [auth() for auth in self.authentication_classes]  # 一个列表推导式

# l) 如果在自己定义的视图类中写了authentication_classes=[类1,类2]----》
class APIView(View):
    # The following policies may be set at either globally, or per-view.
    authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES

# m) Request的self.authenticators就变成了我们配置的一个个类的对象

2.2 Request类的: self._authenticate(self) 方法

2) self._authenticate(self):Request类的方法
def _authenticate(self):
     for authenticator in self.authenticators: # BookView中配置的一个个类的对象
            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

3) 只要在视图类中配置authentication_classes = [MyAuthen.LoginAuth, ]
	就会执行上面的方法,执行认证

3 自定义认证类(重点)

from rest_framework.exceptions import AuthenticationFailed
from rest_framework.authentication import SessionAuthentication, BasicAuthentication

# 1 使用(GET方法)
1)-定义一个类,继承BaseAuthentication
class LoginAuth(BaseAuthentication):
    def authenticate(self, request):
        token = request.GET.get('token')
        res = models.UserToken.objects.filter(token=token).first()
        if res:
            return 元组
        else:
            raise AuthenticationFailed('您没有登录')

2)-重写authenticate方法

3)-局部使用和全局使用
	-局部:在视图类中配置(只要配置了,就是登录以后才能访问,没配置,不用登录就能访问)
		authentication_classes = [MyAuthen.LoginAuth, ]
	-全局
        REST_FRAMEWORK = {
        "DEFAULT_AUTHENTICATION_CLASSES": ["app01.MyAuthen.LoginAuth", ]
        }
        
4)-注意:
	1) 认证类,认证通过可以返回一个元组,有两个值,第一个值会给,request.user,第二个值会个request.auth
    2) 认证类可以配置多个,按照从前向后的顺序执行,如果前面有返回值,认证就不再继续往下走了

4 认证功能局部使用和全局使用

1) 全局使用(所有接口,都需要登录才能访问)
	-在配置文件中
        REST_FRAMEWORK = {
        "DEFAULT_AUTHENTICATION_CLASSES": ["app01.MyAuthen.LoginAuth", ]
        }

2) 局部使用
	-在想局部使用的视图类上
	authentication_classes = [MyAuthen.LoginAuth,]

3) 局部禁用
	-在想禁用的视图类上
    authentication_classes = []

5 内置认证方案(需要配合权限使用)

5.1 全局使用

# 可以在配置文件中配置全局默认的认证方案
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.SessionAuthentication',  # session认证
        'rest_framework.authentication.BasicAuthentication',   # 基本认证
    )
}

5.2 局部使用

# 也可以在每个视图中通过设置authentication_classess属性来设置
from rest_framework.authentication import SessionAuthentication, BasicAuthentication
from rest_framework.views import APIView

class ExampleView(APIView):
    # 类属性
    authentication_classes = [SessionAuthentication, BasicAuthentication]
    ...

5.3 认证失败的返回值

# 认证失败会有两种可能的返回值:
-401 Unauthorized       # 未认证
-403 Permission Denied  # 权限被禁止

二 def权限Permissions

# 权限控制可以限制用户对于视图的访问和对于具体数据对象的访问。
    -在执行视图的dispatch()方法前,会先进行视图访问权限的判断
    -在通过get_object()获取具体对象时,会进行模型对象访问权限的判断

1 自定义权限功能(重点)

1) 登录成功以后,超级用户可以干某些事,普通用户不能干---》超级用户可以查看某些接口,普通用户不能查看

2) 使用:写一个类继承BasePermission,重写has_permission
    class SuperPermission(BasePermission):
        def has_permission(self, request, view):
            # Return `True` if permission is granted, `False` otherwise.
            # 超级用户可以访问,除了超级用户以外,都不能访问
            if request.user.user_type == '1':
                return True
            else:
                return False
       
3) 局部使用和全局使用
	-在想局部使用的视图类上
	permission_classes = [MyAuthen.SuperPermission]
    -全局使用
      REST_FRAMEWORK = {
        "DEFAULT_PERMISSION_CLASSES": ["app01.MyAuthen.SuperPermission", ]
        }
     -局部禁用
    permission_classes = []

2 权限功能局部使用和全局使用

# 1 使用方式
1)-在想局部使用的视图类上
	permission_classes = [MyAuthen.SuperPermission]

2)-全局使用
	REST_FRAMEWORK = {
        "DEFAULT_PERMISSION_CLASSES": ["app01.MyAuthen.SuperPermission", ]
        }

3)-局部禁用
    permission_classes = []

3 内置的权限类

# 内置权限类
from rest_framework.permissions import AllowAny,IsAuthenticated,IsAdminUser,IsAuthenticatedOrReadOnly
    - AllowAny          # 允许所有用户
    - IsAuthenticated   # 仅通过认证的用户
    - IsAdminUser       # 仅管理员用户
    - IsAuthenticatedOrReadOnly  # 已经登陆认证的用户可以对数据进行增删改操作,没有登陆认证的只能查看数据。

4 权限源码分析

4.1 源码分析

# 1 源码分析
	-APIView的dispatch---》APIView的initial---》APIView的check_permissions(request)
        for permission in self.get_permissions(): # 权限类对象放到列表中
        if not permission.has_permission(request, self):
            self.permission_denied(
                 request,
                 message=getattr(permission, 'message', None),
                 code=getattr(permission, 'code', None)
                )

4.2 错误信息的中文显示

# 2 错误信息的中文显示
	在权限类中加一个 message=字符串  ————》message=getattr(permission, 'message', None),

5 模型层choice字段使用(重点)

1) 模型表:Student表,写接口应该选择继承哪个视图类
2) 推荐使用自动生成路由的方式(继承ViewSetMixin及它的字类)
3) 但是目前来说,你先实现功能即可(至于选择哪个,慢慢体会)

4) choice的使用
	-在模型类中使用
    sex = models.SmallIntegerField(choices=((1, '男'), (2, '女'), (3, '未知')), default=1)
    -在视图类中,在序列化类中
    	-get_字段名_dispaly()的方法,该方法获得choice字段对应的数据

三 drf限流Throttling

1 自定义频率类(分析,了解)

1.1 自定义频率类用途及使用

1) 限制某个人,某个ip的访问频次

2) 自定义频率类及使用
from rest_framework.throttling import BaseThrottle
class MyThrottle(BaseThrottle):
    VISIT_RECORD = {}  # 存用户访问信息的大字典
    def __init__(self):
        self.history = None
    def allow_request(self,request,view):
        # 根据ip进行频率限制,每分钟只能访问3次
        # 限制的逻辑
        '''
        #(1)取出访问者ip
        #(2)判断当前ip不在访问字典里,添加进去,并且直接返回True, 表示第一次访问,在字典里,继续往下走
        #(3)循环判断当前ip的列表,有值,并且当前时间减去列表的最后一个时间大于60s,把这种数据pop掉,这样列表中只有60s以内的访问时间,
        #(4)判断,当列表小于3,说明一分钟以内访问不足三次,把当前时间插入到列表第一个位置,返回True,顺利通过
        #(5)当大于等于3,说明一分钟内访问超过三次,返回False验证失败
        '''
        # (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])   

1.2 局部使用与全局使用

1)-局部使用
	throttle_classes = [auth.MyThrottle,]

2)-全局使用
	REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_CLASSES':['app01.auth.MyThrottle',],
}

2 内置频率类使用

2.1 内置频率类

-BaseThrottle:      # 基类
-AnonRateThrottle:  # 限制匿名用户的访问次数
-SimpleRateThrottle:# 我们自定义扩写它
-ScopedRateThrottle:
-UserRateThrottle:  # 限制登录用户访问次数

2.2 扩展内置频率类(重点记住)

# 扩展内置频率类(重点记住)
1)-写一个类,继承SimpleRateThrottle
    class MySimpleThrottle(SimpleRateThrottle):
        scope = 'xxx'
        def get_cache_key(self, request, view):
            #以ip限制
            return self.get_ident(request)

2)-setting.py中配置
    REST_FRAMEWORK = {
        'DEFAULT_THROTTLE_RATES' : {
            'xxx':'10/m'  # key跟scope对应,value是一个时间
        }
    }

3)-局部使用与全局使用

2.3 源码分析

# 源码分析
-继承SimpleRateThrottle---》allow_request(跟咱们写的一样)

2.4 其它内置频率类

# 其它内置频率类
1)-限制未登录用户的频率(AnonRateThrottle)(根据ip限制)
-使用:
	-局部使用,全局使用
	-setting.py中配置
	'DEFAULT_THROTTLE_RATES' : {
    'anon':'1/m'
	}

2)-限制登录用户访问次数 UserRateThrottle(根据用户id限制)
-使用:
    -局部使用,全局使用
    -setting.py中配置
    'DEFAULT_THROTTLE_RATES' : {
        'user':'1/m'
    }

3)-ScopedRateThrottle(有兴趣看一下,没有就不看了) 

四 过滤Filter与排序

1 内置,第三方过滤功能(次重点)

1) 过滤:筛选查询结果

2) 内置筛选的使用
	-在视图类中配置
        filter_backends = [SearchFilter,]
        search_fields = ('name',) # 表模型中的字段
    -查询的时候
    	http://127.0.0.1:8000/students/?search=e
                
3) 第三方扩展的过滤功能
	-pip3 install django-filter  :最新版本(2.4.0)要跟django2.2以上搭配
    
    -在视图类中配置
        filter_backends = [DjangoFilterBackend,]
    	filter_fields = ['name','age']
    -查询的时候
    	http://127.0.0.1:8000/students/?name=lqz&age=18

2 排序功能(次重点)

-在视图类中配置
    filter_backends = [OrderingFilter,]
    ordering_fields = ['id','age']

-查询的时候
	http://127.0.0.1:8000/students/?ordering=-age
            
            
 ### 过滤后再排序
  -在视图类中配置
    filter_backends = [OrderingFilter,DjangoFilterBackend]
    ordering_fields = ('id', 'age')
    filter_fields = ['name','age']

  -查询的时候
	http://127.0.0.1:8000/students/?name=lqz&age=19&ordering=-age,-id

3 自定制过滤器

3.1 使用原理

1)-查询所有才会有过滤---》list才需要过滤---》queryset = self.filter_queryset(self.get_queryset())---》GenericAPIView-->filter_queryset

3.2 使用方法

# 基于django-filter扩写

1) 写一个类MyFilter,继承BaseFilterBackend

2) 重写filter_queryset方法,在该方法内部进行过滤(自己设置的过滤条件)
    def filter_queryset(self, queryset):
        for backend in list(self.filter_backends):
            queryset = backend().filter_queryset(self.request, queryset, self)
            return queryset
# self.get_queryset() 就是 models.表名.objects.all()

3) 返回queryset对象(过滤后的queryset对象)

4) 配置在视图类中
	filter_backends = [MyFilter,]

五 拓展

1) select_related的使用
# 基于对象的跨表查询,可以减少查询数据库的次数
articleList=models.Article.objects.select_related("category").all()
for article_obj in articleList:
        #  Doesn't hit the database, because article_obj.category
        #  has been prepopulated in the previous query.
        #不再查询数据库,因为第一次查询,数据已经填充进去了
        print(article_obj.category.title)
        
2) mysql的悲观锁和乐观锁

3) drf内部内置了一些认证类,分别干了什么事
	待填坑

标签:self,request,认证,authentication,限流,user,权限,def,drf
来源: https://www.cnblogs.com/chaochaofan/p/14932335.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有