drf篇:认证类、权限类、频率类

  • 认证
  • 权限
  • 频率
  • 认证类源码分析

1 认证

# 登录认证---》某个接口必须登录后才能房屋
# 登录接口---》登录成功返回随机字符串---》携带随机字符串【认证】 通过,再继续访问某个接口
# 读APIView源码时,三大认证---》视图类的方法之前执行的----》drf提供了很方便的写认证


# 写一个登录接口
    -用户表,用户token表
    -前端传入用户名密码----》视图类---》登录方法---》校验用户名密码是否正确,如果正确 生成随机字符串存入数据库---》把随机字符串返回给前端
    
# 随便写个接口---》登录后才能访问
# 写认证类
    -第一步:写一个类,继承BaseAuthentication
    -第二步:在类中重写authenticate方法
    -第三步:在方法中做验证,如果通过,返回两个值,如果不通过,抛AuthenticationFailed的异常
    
# 使用认证类
    -局部使用-->视图类中
        class BookView(APIView):
            authentication_classes = [LoginAuth,]
    -全局使用---》配置文件
    	REST_FRAMEWORK = {
            # 全局使用认证类
            'DEFAULT_AUTHENTICATION_CLASSES':['app01.auth.LoginAuth',]
        }
        
    -局部禁用
     class BookView(APIView):
         authentication_classes = []
 
# 前端调用接口时
http://127.0.0.1:8000/user/books?token=27954a0e-9b0b-442d-8aff-839ec9f70960

1.1 登录接口

模型类

class User(models.Model):
    username = models.CharField(max_length=32)
    password = models.CharField(max_length=32)
class UserToken(models.Model):
    token = models.CharField(max_length=32)  # 用户登录成功签发的随机字符串
    user = models.OneToOneField(to='User',on_delete=models.CASCADE)

路由

from rest_framework.routers import SimpleRouter
from .views import UserView,BookView
router=SimpleRouter()
router.register('user',UserView,'user')
urlpatterns = [
]
urlpatterns+=router.urls

视图类

class UserView(ViewSet):
    authentication_classes = []  # 局部禁用
    # 127.0.0.1:8080/user/user/login--->post
    @action(methods=['post', ], detail=False)
    def login(self, request):
        # 取出用户名密码---》去数据库查询---》有就登录成功(生成随机字符串存入数据),没有就登录失败
        username = request.data.get('username')
        password = request.data.get('password')
        user = User.objects.filter(username=username, password=password).first()
        if user:
            # 登录成功
            # 生成一个随机字符串---》uuid--》生成一个随机不重复的值
            token = str(uuid.uuid4())  # python 动态强类型
            # 存到UserToken表中---》两种情况:之前有了就更新,之前没有就新增
            # 有就更新,没有就修改
            # defaults=None, **kwargs:传入的去查询,如果能查到使用defaults给的更新
            UserToken.objects.update_or_create(user=user, defaults={'token': token})
            # UserToken.objects.update_or_create( defaults={'token': token},kwargs={'user':user})
            return Response({'code': 100, 'msg': '登录成功', 'token': token})

        else:
            return Response({'code': 101, 'msg': '用户名或密码错误'})

1.2 认证类


from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from .models import UserToken
class LoginAuth(BaseAuthentication):
    def authenticate(self, request):
        # 做验证,验证用户是否登录
        # 放到请求地址中/请求头中
        token=request.query_params.get('token')
        # 去数据库查 token在不在
        user_token=UserToken.objects.filter(token=token).first()
        if user_token:
            # 验证通过了,返回两个值,第一个值是当前登录用户,第二个值是token
            return user_token.user,token
        else:
            # 验证失败
            raise AuthenticationFailed('小伙子,您没有登录,不能访问')

2 权限

# 登录成功了,但是有的接口区分权限,有的人有权限,有的人没权限
# 用户表新增一个 用户类型字段:超级管理员,普通管理员,普通用户


# 权限类的写法
    -第一步:写一个类,继承BasePermission
    -第二步:重写has_permission方法
    -第三步:在has_permission中,进行权限的判断,如果有权限,返回True,如果没有权限,返回False,返回的中文提示信息,使用message字段标识
    
    
#权限类的使用
	-局部使用-->视图类中
        class BookView(APIView):
             permission_classes = [PermissionAuth,]
    -全局使用---》配置文件
    	REST_FRAMEWORK = {
            # 全局使用认证类
            'DEFAULT_PERMISSION_CLASSES':['app01.auth.PermissionAuth',]
        }
        
    -局部禁用
     class BookView(APIView):
         permission_classes = []

3 频率类

# 某个接口,限制访问频率----》可以根据IP,用户id
# 频率类的编写
    -第一步:写一个类,继承SimpleRateThrottle
    -第二步:重写get_cache_key方法
    -第三步:返回什么,就以什么做限制
    -第四步:写一个类属性
    	-scope = '3_min'
    -第五步:配置文件中配置
    	"DEFAULT_THROTTLE_RATES" : {
            # 频率类中scope对应的值
            '3_min':'3/m'   # 数字/s m h  d
        }
        
   -第六步:局部和全局使用
	局部用:视图类中
    	class BookView(APIView):
    		throttle_classes = [IPThrottle, ]
    全局用:配置文件
    	REST_FRAMEWORK = {
            "DEFAULT_THROTTLE_RATES": {
                # 频率类中scope对应的值
                '3_min': '3/m'  # 数字/s m h  d
            },
            'DEFAULT_THROTTLE_CLASSES':['app01.throttling.IPThrottle',]
        }

3.1 使用

频率类

class IPThrottle(SimpleRateThrottle):
    # 写一个类属性,字符串
    scope = '3_min'
    def get_cache_key(self, request, view):
        # return 什么就一什么做限制, 返回ip   返回用户id
        return request.META.get('REMOTE_ADDR')
        # return request.user.id  # 返回用户id

视图类

class BookView(APIView):
    throttle_classes = [IPThrottle, ]

    def get(self, request):
        return Response('ok')

    def throttled(self, request, wait):  # 定义函数,指定显示的内容
        from rest_framework.exceptions import Throttled
        class MyThrottled(Throttled):
            default_detail = '超限制了'
            extra_detail_singular = '还有 {wait} 秒.'
            extra_detail_plural = '出了 {wait} 秒.'

        raise MyThrottled(wait)

配置文件

# 自定义频率类的配置
REST_FRAMEWORK = {
    "DEFAULT_THROTTLE_RATES": {
        # 频率类中scope对应的值
        '3_min': '3/m',  # 数字/s m h  d
        'anon':'4/h',
        'user':'5/m'
    },
    'DEFAULT_THROTTLE_CLASSES':['app01.throttling.IPThrottle',]

}


# 内置频率类的配置
REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_CLASSES': [
        'rest_framework.throttling.AnonRateThrottle',
        'rest_framework.throttling.UserRateThrottle'
    ],
    'DEFAULT_THROTTLE_RATES': {
        'anon': '10/day',
        'user': '50/day'
    }
}

4 drf内置认证类,权限类,频率类

# 内置的认证---》跟咱们项目都不贴和,咱们不用,咱们自己根据自己的规则写
    -SessionAuthentication:之前老的session认证登录方式用,后期都不用了
    -BasicAuthentication :基本认证方式,咱们不用
    -TokenAuthentication :使用token认证方式,有用,但是咱们也是自己写的
    
    
# 内置的权限类
    -IsAdminUser :校验是不是auth的超级管理员权限
    -IsAuthenticated:后面会用到,验证用户是否登录了,登录了才有权限,没登录就没有权限
    -IsAuthenticatedOrReadOnly:知道有这个东西即可
    
# 内置的频率类
    -UserRateThrottle :限制登录用户的频率,需要配置配置文件
    -AnonRateThrottle:登录用户不限制,未登录用户限制,需要配置配置文件

5 认证类源码分析(了解)

####### 不要无脑按住ctrl建点击查找,因为方法,属性的查找顺序是按照 mro列表查找顺序来的

#  研究第一个点:三大认证的执行顺序---》入口---》APIView的dispathc----》三大认证
	-dispatch中第 497行 --》self.initial(request, *args, **kwargs)
    -self是谁?self是个对象,是谁的对象? 是视图类的对象---》BookView这些视图类
    -APIView的initial方法的第414行
        self.perform_authentication(request)  # 认证
        self.check_permissions(request)     # 权限
        self.check_throttles(request)      # 频率
        
        
        
# 为什么写了认证类,配置在视图类上,就会走认证?---》入口-->self.perform_authentication(request)
      -第一步:self.perform_authentication源码
    def perform_authentication(self, request):
     	request.user
     -第二步:新的request类中 user(属性?方法?)
    	from rest_framework.request import Request
         @property
        def user(self):
            if not hasattr(self, '_user'):
                with wrap_attributeerrors():
                    self._authenticate()  # 最开始,是没有_user的,执行这句
            return self._user
    -第三步:self._authenticate()  --》self是Request的对象---》Request的_authenticate方法
	    def _authenticate(self):
            # authenticator配置的认证类的对象
            for authenticator in self.authenticators: # 你配置在视图类上authentication_classes = [你写的认证类,]----》[你写的认证类(),你写的认证类2()]
                try:
                    # 调用认证类对象的authenticate方法,传了几个?2个,一个认证类自己,一个是self:Request类的对象
                    user_auth_tuple = authenticator.authenticate(self)
                except exceptions.APIException: # 抛的是AuthenticationFailed,捕获的是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

	-第四步: self.authenticators到底是啥---》你配置在视图类上authentication_classes = [你写的认证类,]----》[你写的认证类(),你写的认证类2()]
    	-Reqeust这个类实例化的时候,传入的,如果不传就是空元组
        -找Request的实例化---》dispatch中包装了新的Request
        	-request = self.initialize_request(request, *args, **kwargs)
            -authenticators=self.get_authenticators(),
            
        -APIView中get_authenticators---》268行
          def get_authenticators(self):
             # self.authentication_classes配在视图类上的认证类列表---》认证类对象列表
        	return [auth() for auth in self.authentication_classes]

6 权限,频率执行源码分析(了解)

6.1 权限源码执行流程

# APIView的dispatch---》self.initial(request, *args, **kwargs)---》415行self.check_permissions(request)--->
    def check_permissions(self, 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)
                )

6.2 频率源码执行流程

# APIView的dispatch---》self.initial(request, *args, **kwargs)---》416行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())
        if throttle_durations:
            durations = [
                duration for duration in throttle_durations
                if duration is not None
            ]

            duration = max(durations, default=None)
            self.throttled(request, duration)
            
# 你写的频率类一定要重写allow_request,返回True就是没有频率限制住,返回False就是被频率限制了

# 咱们第一天写的,继承SimpleRateThrottle---》它肯定重写了allow_request---》今天写的逻辑跟源码一样
SimpleRateThrottle---》allow_request---》
	def __init__(self):
        if not getattr(self, 'rate', None):
            # self.rate 现在是  '3/m'
            self.rate = self.get_rate()
        # 3                   60
        self.num_requests, self.duration = self.parse_rate(self.rate)
    def allow_request(self, request, view):
        if self.rate is None:  # 只要配在文件配了,它就有值,在init中
            return True

        # 现在的唯一字符串
        # ip地址
        self.key = self.get_cache_key(request, view)
        if self.key is None:
            return True

        # [时间2,时间1]
        self.history = self.cache.get(self.key, [])
        self.now = self.timer()
        while self.history and self.history[-1] <= self.now - self.duration:
            self.history.pop() #把所有超过60的数据都剔除,self.history只剩60s以内的访问时间
        if len(self.history) >= self.num_requests:  #大于等于配置的数字3 
            return self.throttle_failure() # return False
        return self.throttle_success()  # 把当前时间插入,retrun True

7 自定义频率类

# 自定义的逻辑
#(1)取出访问者ip   {192.168.1.12:[访问时间3,访问时间2,访问时间1],192.168.1.12:[],192.168.1.14:[]}
#(2)判断当前ip不在访问字典里,添加进去,并且直接返回True,表示第一次访问,在字典里,继续往下走
#(3)循环判断当前ip的列表,有值,并且当前时间减去列表的最后一个时间大于60s,把这种数据pop掉,这样列表中只有60s以内的访问时间,
#(4)判断,当列表小于3,说明一分钟以内访问不足三次,把当前时间插入到列表第一个位置,返回True,顺利通过
#(5)当大于等于3,说明一分钟内访问超过三次,返回False验证失败

7.1 具体实现

from rest_framework.throttling import BaseThrottle
class MyThrottling():
    VISIT_RECORD = {}

    def __init__(self):
        self.history = None
    def allow_request(self, request, view):
        # (1)取出访问者ip
        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])

补充

# python 是动态强类型语言    
# go    是静态强类型语言
# java   是静态强类型语言---》隐士类型转换
#js     是动态弱类型语言

# 动态:解释型语言
# 强弱指的是:数据类型强:不同类型之间不允许之间运算
# 弱类型:不同类型之间不需要转换可以之间运算


# 鸭子类型(面试重点)
	-接口概念:规范子类的行为   你当成:父类,有一些方法  ---》Duck类,speak,run方法
    -只要子类,继承了Duck,我们就认为,这些子类,他们是同一类,都是鸭子Duck这个类
    -python不推崇这个,推崇鸭子类型
    -现在只要有一个类,中有speak和run 方法 ,这个类就是鸭子类
    
    -abc装饰器,装饰父类中的方法,子类必须实现该方法,如果不实现,就抛异常---》正好违背了鸭子类型
    -djagno中常用的方式,父类抛异常的方式,子类必须重写该方法:
       def authenticate(self, request):
        	raise NotImplementedError(".authenticate() must be overridden.")
# 函数
	def add(a:int,b:int):->int
        return a+b


# 关于编辑器---》IDE
	-python开发用pycharm(主流--》收费),vscode---》微软出的,免费,操作习惯跟pycharm不一样
    -数据分析 :Jupyter 读作:朱庇特 或 朱比特、朱皮特
    
    -关于编辑器的分类
    -jetbrains公司全家桶---》无论你做什么语言开发---》都可以用全家桶之一---》专业做编辑器的--》java开发---》运行在jvm之上
    	-c开发:clin
        -go开发:goland
        -python:pycharm
        -前端开发:webstror
        -php:phpstorm
        -java:idea  eclipse  MyEclipse
        -安卓:AndroidStadio ---》谷歌公司买了jetbrains版权+ADT=AndroidStadio
    -微软的vscode---》免费的---》轻量级,占内存小---》丰富性不如jetbrains--》装第三方插件
    	-go
        -python
    -vim+第三方插件---》跟pycharm一样的功能

# 健壮性----鲁棒性
posted @ 2022-06-21 15:05  马氵寿  阅读(98)  评论(0)    收藏  举报