一、三大认证功能分析

  1. 1APIView dispath(self, request, *args, **kwargs)
  2. 2dispath方法内 self.initial(request, *args, **kwargs) 进入三大认证
  3. # 认证组件:校验用户 - 游客、合法用户、非法用户
  4. # 游客:代表校验通过,直接进入下一步校验(权限校验)
  5. # 合法用户:代表校验通过,将用户存储在request.user中,再进入下一步校验(权限校验)
  6. # 非法用户:代表校验失败,抛出异常,返回403权限异常结果
  7. # 只要通过认证不管是游客还是登录用户,request.user都有值
  8. self.perform_authentication(request)
  9.  
  10. # 权限组件:校验用户权限 - 必须登录、所有用户、登录读写游客只读、自定义用户角色
  11. # 认证通过:可以进入下一步校验(频率认证)
  12. # 认证失败:抛出异常,返回403权限异常结果
  13. self.check_permissions(request)
  14.  
  15. # 频率组件:限制视图接口被访问的频率次数 - 限制的条件(IP、id、唯一键)、频率周期时间(s、m、h)、频率的次数(3/s)
  16. # 没有达到限次:正常访问接口
  17. # 达到限次:限制时间内不能访问,限制时间达到后,可以重新访问
  18. self.check_throttles(request)

二、认证组件

  1. 认证组件:校验认证字符串,得到request.user
  2. 没有认证字符串,直接放回None,游客
  3. 有认证字符串,但认证失败抛异常,非法用户
  4. 有认证字符串,且认证通过返回 用户,认证信息 元组,合法用户
  5. 用户存放到request.user | 认证信息存放到request.auth

1.源码分析1

  1. 点进 self.perform_authentication(request)
    发现只有request.user
    也没有接收这个值也没有返回,那么这一定是个方法,一定调取了某个函数,不然还怎么玩
  2. 那么我们就思考request是谁的?往前面找发现在dispath
    request = self.initialize_request(request, *args, **kwargs),
    也就是说能走到认证这一步,request已经完成二次封装,那必然是drf自己的,我们到drfrequest中找

drf的 request.py / Request / user(self)

  1. drfrequest.pyRequest类中有两个user,因为没有值所以肯定走得是getuserself),如果是request.user = 111,那就是走setuserselfvalue
  2.  
  3. Request类的 方法属性 user get方法 => self._authenticate() 完成认证
  1. def user(self):
  2. if not hasattr(self, '_user'):
  3. with wrap_attributeerrors():
  4. # 没用户,认证出用户
  5. self._authenticate() # 点进去
  6. # 有用户直接返回
  7. return self._user
  1. 认证的细则:
  2. # 做认证
  3. def _authenticate(self):
  4. # 遍历拿到一个个认证器,进行认证
  5. # self.authenticators是配置的一堆认证类,产生的认证类对象,组成的 list,一堆认证器
  6. for authenticator in self.authenticators: # 这个在request下面肯定self就是requst对象的,我们还去diepatch中找二次封装的request
  7. try:
  8. # 认证器(对象)调用认证方法authenticate(认证类对象self, request请求对象)
  9. # 返回值:登陆的用户与认证的信息组成的 tuple
  10. # 该方法被try包裹,代表该方法会抛异常,抛异常就代表认证失败
  11. user_auth_tuple = authenticator.authenticate(self)
  12. except exceptions.APIException:
  13. self._not_authenticated()
  14. raise
  15.  
  16. # 返回值的处理
  17. if user_auth_tuple is not None:
  18. self._authenticator = authenticator
  19. # 解压赋值,如何有返回值,就将 登陆用户 与 认证信息 分别保存到 request.user、request.auth
  20. self.user, self.auth = user_auth_tuple
  21. return # 这里有return 说明有返回值就直接结束了,不会走下面的代码
  22. # 如果返回值user_auth_tuple为空,代表认证通过,但是没有 登陆用户 与 登陆认证信息,代表游客
  23. self._not_authenticated()

2. request 源码分析2

dispatch

  1. request = self.initialize_request(request, *args, **kwargs) # 点进去
  1. def initialize_request(self, request, *args, **kwargs):
  2. parser_context = self.get_parser_context(request)
  3. return Request(
  4. request,
  5. parsers=self.get_parsers(),
  6. authenticators=self.get_authenticators(), # 点进去
  7. negotiator=self.get_content_negotiator(),
  8. parser_context=parser_context
  9. )
  1. def get_authenticators(self):
  2. return [auth() for auth in self.authentication_classes] # 从一堆认证类中遍历出每一个类,类加括号实例化成一个个认证器对象,即一堆认证器
  1. class APIView(View):
  2.      ...
  3. authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES # 最终来到APIView源码顶部的一大堆类属性,这些东西一定在drf的settings里面
  4.         ...
  1. 'DEFAULT_AUTHENTICATION_CLASSES': [
  2. 'rest_framework.authentication.SessionAuthentication',
  3. 'rest_framework.authentication.BasicAuthentication'
  4. ], # 在drf的配置里我们找到这个,直接拿走放进我们项目的settings里面,之后开始定制

3. 原生配置 源码分析3

我们分析上面两个配置的源码学习套路来自定义

rest_framework / authentication.py / SessionAuthentication
  1. def authenticate(self, request):
  2. #解析出了用户
  3. user = getattr(request._request, 'user', None)
  4.  
  5. #没有解析出用户返回None
  6. if not user or not user.is_active:
  7. return None
  8.  
  9. # 解析出用户后,重新启用csrf认证,所以以后我们不用session认证,因为他需要csrf来解码
  10. # 如果scrf认证失败,就会异常,就是非法用户
  11. self.enforce_csrf(request)
  12.  
  13. # csrf通过,就是合法用户,返回用户和none
  14. return (user, None)
rest_framework / authentication.py / BasicAuthentication
  1. def authenticate(self, request):
  2. # 获取认证信息,该认证信息是两段式,合法格式是 ‘basic 认证字符串’
  3. auth = get_authorization_header(request).split()
  4.  
  5. # 没有认证信息就是游客
  6. if not auth or auth[0].lower() != b'basic':
  7. return None
  8.  
  9. # 有认证信息,信息有误就是非法用户
  10. if len(auth) == 1:
  11. msg = _('Invalid basic header. No credentials provided.')
  12. raise exceptions.AuthenticationFailed(msg)
  13. # 超过两段,也抛异常
         elif len(auth) > 2:
  14. msg = _('Invalid basic header. Credentials string should not contain spaces.')
  15. raise exceptions.AuthenticationFailed(msg)
  16.  
  17. try:
  18. auth_parts = base64.b64decode(auth[1]).decode(HTTP_HEADER_ENCODING).partition(':')
  19. except (TypeError, UnicodeDecodeError, binascii.Error):
  20. msg = _('Invalid basic header. Credentials not correctly base64 encoded.')
  21. raise exceptions.AuthenticationFailed(msg)
  22.  
  23. userid, password = auth_parts[0], auth_parts[2]
  24. # 认证信息处理出用户主键和密码,进一步得到用户对象
  25. # 得不到或是非活跃用户,代表非法,一切正常才代表 合法用户
  26. return self.authenticate_credentials(userid, password, request)
  1. def authenticate_credentials(self, userid, password, request=None):
  2. credentials = {
  3. get_user_model().USERNAME_FIELD: userid,
  4. 'password': password
  5. }
  6. user = authenticate(request=request, **credentials)
  7.  
  8. # 得不到或是非活跃用户,代表非法,一切正常才代表合法用户
  9. if user is None:
  10. raise exceptions.AuthenticationFailed(_('Invalid username/password.'))
  11.  
  12. if not user.is_active:
  13. raise exceptions.AuthenticationFailed(_('User inactive or deleted.'))
  14.  
  15. return (user, None)

4.自定义认证类

  1. 1) 创建继承BaseAuthentication的认证类
  2. 2) 重写authenticate(self, request)方法,自定义认证规则
  3. 3) 实现体根据认证规则 确定游客、非法用户、合法用户
  4. 认证规则:
  5. i.没有认证信息返回None(游客)
  6. ii.有认证信息认证失败抛异常(非法用户)
  7. iii.有认证信息认证成功返回用户与认证信息元组(合法用户)
  8. 4) 完成视图类的全局(settings文件中)或局部(确切的视图类)配置

5. 自定义认证类例子

新建 authentications.py

  1. from rest_framework.authentication import BaseAuthentication
  2. from rest_framework.exceptions import AuthenticationFailed
  3. from . import models
  4. class MyAuthentication(BaseAuthentication):
  5. """
  6. 同前台请求头拿认证信息auth(获取认证的字段要与前台约定)
  7. 没有auth是游客,返回None
  8. 有auth进行校验
  9. 失败是非法用户,抛出异常
  10. 成功是合法用户,返回 (用户, 认证信息)
  11. """
  12. def authenticate(self, request):
  13. # 前台在请求头携带认证信息,
  14. # 且默认规范用 Authorization 字段携带认证信息,
  15. # 后台固定在请求对象的META字段中 HTTP_AUTHORIZATION 获取
  16. auth = request.META.get('HTTP_AUTHORIZATION', None)
  17.  
  18. # 处理游客
  19. if auth is None:
  20. return None
  21.  
  22. # 设置一下认证字段小规则(两段式):"auth 认证字符串"
  23. auth_list = auth.split()
  24.  
  25. # 校验合法还是非法用户
  26. if not (len(auth_list) == 2 and auth_list[0].lower() == 'auth'):
  27. raise AuthenticationFailed('认证信息有误,非法用户')
  28.  
  29. # 合法的用户还需要从auth_list[1]中解析出来
  30. # 注:假设一种情况,信息为abc.123.xyz,就可以解析出admin用户;实际开发,该逻辑一定是校验用户的正常逻辑
  31. if auth_list[1] != 'abc.123.xyz': # 校验失败
  32. raise AuthenticationFailed('用户校验失败,非法用户')
  33.  
  34. user = models.User.objects.filter(username='admin').first() # 这里是示范,写死了找这个用户
  35.  
  36. if not user:
  37. raise AuthenticationFailed('用户数据有误,非法用户')
  38. return (user, None)

api / views.py

  1. from rest_framework.views import APIViewfrom utils.response import APIResponse
  2.  
  3. class TestAPIView(APIView):
  4. def get(self, request, *args, **kwargs):
  5. # 如果通过了认证组件,request.user就一定有值
  6. # 游客:AnonymousUser
  7. # 用户:User表中的具体用户对象
  8. print(request.user) # admin
  9. return APIResponse(0, 'test get ok')

settings.py

  1. # drf配置
  2. REST_FRAMEWORK = {
  3. # 全局配置异常模块
  4. 'EXCEPTION_HANDLER': 'utils.exception.exception_handler',
  5. # 认证类配置
  6. 'DEFAULT_AUTHENTICATION_CLASSES': [
  7. # 'rest_framework.authentication.SessionAuthentication',
  8. # 'rest_framework.authentication.BasicAuthentication',
  9. 'api.authentications.MyAuthentication',
  10. ],
  11. # 权限类配置
  12. 'DEFAULT_PERMISSION_CLASSES': [
  13. 'rest_framework.permissions.AllowAny',
  14. ],
  15. }

子路由

  1. from django.conf.urls import url
  2. from . import views
  3. urlpatterns = [
  4. url(r'^test/$', views.TestAPIView.as_view()),
  5. url(r'^test1/$', views.TestAuthenticatedAPIView.as_view()),
  6. url(r'^test2/$', views.TestAuthenticatedOrReadOnlyAPIView.as_view()),
  7. url(r'^test3/$', views.TestAdminOrReadOnlyAPIView.as_view()),
  8. ]
  1. # 家庭作业
  2. # 1) 可以采用脚本基于auth组件创建三个普通用户:models.User.objects.create_user()
  3. # 注:直接写出注册接口更好
  4. # 2) 自定义session表:id,u_id,token,为每个用户配置一个固定人认证字符串,可以直接操作数据库
  5. # 注:在注册接口中实现更好
  6. # 3) 自定义认证类,不同的token可以校验出不同的登陆用户

三、权限组件

1.源码分析1

  1.   # 入口 dispatch三大认证中:
  2. self.check_permissions(request)
  3. 认证细则:
  4. def check_permissions(self, request):
  5. # 遍历权限对象列表得到一个个权限对象(权限器),进行权限认证
  6. for permission in self.get_permissions():
  7. # 权限类一定有一个has_permission权限方法,用来做权限认证的
  8. # 参数:权限对象self、请求对象request、视图类对象
  9. # 返回值:有权限返回True,无权限返回False
  10. if not permission.has_permission(request, self):
  11. self.permission_denied(
  12. request, message=getattr(permission, 'message', None)
  13. )
  1. def get_permissions(self):
  2. # 全局局部配置
  3. return [permission() for permission in self.permission_classes] # 从一堆权限类中遍历出每一个类,类加括号实例化成一个个权限器对象,即一堆权限器
  1. class APIView(View):
  2.  
  3.           ...
  4. permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES # 最终来到APIView源码顶部的一大堆类属性,这些东西一定在drf的settings里面
  1.           ...
  1.  
  1. 'DEFAULT_PERMISSION_CLASSES': [
    'rest_framework.permissions.AllowAny',
    ],
  1. # 在drf的配置里我们找到这个,直接拿走放进我们项目的settings里面,之后开始定制

2. 源码分析3 — 四种权限

rest_framework / permissions 里面有四个类

  1. 1AllowAny:(默认规则)
  2. 认证规则全部返还Truereturn True
  3. 游客与登陆用户都有所有权限
  4.  
  5. 2) IsAuthenticated
  6. 认证规则必须有登陆的合法用户:return bool(request.user and request.user.is_authenticated)
  7. 游客没有任何权限,登陆用户才有权限
  8.  
  9. 3) IsAdminUser
  10. 认证规则必须是后台管理用户:return bool(request.user and request.user.is_staff)
  11. 游客没有任何权限,登陆用户才有权限
  12.  
  13. 4) IsAuthenticatedOrReadOnly
  14. 认证规则必须是只读请求或是合法用户:
  15. return bool(
  16. request.method in SAFE_METHODS or
  17. request.user and
  18. request.user.is_authenticated
  19. )
  20. 游客只读,合法用户无限制
  1. 注:SAFE_METHODS = ('GET', 'HEAD', 'OPTIONS')
  1.  

3.应用上面四个类 设置局部权限

# api/views.py

  1. # 必须登录才能访问
  2. from rest_framework.permissions import IsAuthenticated
  3. class TestAuthenticatedAPIView(APIView):
  4. permission_classes = [IsAuthenticated] # 局部配置,只有一站式网站例如邮箱才会采用全局配置IsAuthenticated
  5. # 当定义全局必须登录才能访问情况下,个别接口不需要登录,只需局部配置 permission_classes = []或 permission_classes = [AllowAny]即可
  6. def get(self, request, *args, **kwargs):
  7. return APIResponse(0, 'test 登录才能访问的接口 ok')
  8.  
  9. # 游客只读,登录无限制
  10. from rest_framework.permissions import IsAuthenticatedOrReadOnly
  11. class TestAuthenticatedOrReadOnlyAPIView(APIView):
  12. permission_classes = [IsAuthenticatedOrReadOnly]
  13.  
  14. def get(self, request, *args, **kwargs):
  15. return APIResponse(0, '读 OK')
  16.  
  17. def post(self, request, *args, **kwargs):
  18. return APIResponse(0, '写 OK')

# settings.py

  1. # 默认全局配置的权限类是AllowAny
  2. REST_FRAMEWORK = {
  3. # 权限类配置
  4. 'DEFAULT_PERMISSION_CLASSES': [
  5. 'rest_framework.permissions.AllowAny',
  6. ],
  7. }

4.自定义权限类

除了上面四个类的权限,我们往往有更高的更复杂的权限需求,这就需要自定义权限了

  1. 1) 创建继承BasePermission的权限类
  2. 2) 实现has_permission方法
  3. 3) 实现体根据权限规则 确定有无权限
  4. 4) 进行全局或局部配置
  5.  
  6. 认证规则
  7. i.满足设置的用户条件,代表有权限,返回True
  8. ii.不满足设置的用户条件,代表有权限,返回False

# utils/permissions.py

  1. from rest_framework.permissions import BasePermission
  2. from django.contrib.auth.models import Group
  3. class MyPermission(BasePermission):
  4. def has_permission(self, request, view):
  5. # 只读接口判断
  6. r1 = request.method in ('GET', 'HEAD', 'OPTIONS')
  7. # group为有权限的分组
  8. group = Group.objects.filter(name='管理员').first()
  9. # groups为当前用户所属的所有分组
  10. groups = request.user.groups.all()
  11. r2 = group and groups
  12. r3 = group in groups
  13. # 读接口大家都有权限,写接口必须为指定分组下的登陆用户
  14. return r1 or (r2 and r3)

api / views.py

  1. # 游客只读,登录用户只读,只有登录用户属于 管理员 分组,才可以增删改
  2. from utils.permissions import MyPermission
  3. class TestAdminOrReadOnlyAPIVie
    w(APIView):
  4. permission_classes = [MyPermission]
  5. # 所有用户都可以访问
  6. def get(self, request, *args, **kwargs):
  7. return APIResponse(0, '自定义读 OK')
  8. # 必须是 自定义“管理员”分组 下的用户
  9. def post(self, request, *args, **kwargs):
  10. return APIResponse(0, '自定义写 OK')

四、频率组件 限制接口的访问频率

源码分析:初始化方法、判断是否有权限方法、计数等待时间方法

1. 入口源码分析

  1. # 1)APIView的dispath方法中的 self.initial(request, *args, **kwargs) 点进去
  2. # 2)self.check_throttles(request) 进行频率认证
  3.  
  4. # 频率组件核心源码分析
  5. def check_throttles(self, request):
  6. throttle_durations = []
  7. # 1)遍历配置的频率认证类,初始化得到一个个频率认证类对象(会调用频率认证类的 __init__() 方法)
  8. # 2)频率认证类对象调用 allow_request 方法,判断是否限次(没有限次可访问,限次不可访问)
  9. # 3)频率认证类对象在限次后,调用 wait 方法,获取还需等待多长时间可以进行下一次访问
  10. # 注:频率认证类都是继承 SimpleRateThrottle 类
  11. for throttle in self.get_throttles():
  12. if not throttle.allow_request(request, self):
  13. # 只要超出频率限制了,allow_request 返回False了,才会调用wait,即得到需要等待的时间
  14. throttle_durations.append(throttle.wait())
  15. if throttle_durations:
  16. durations = [
  17. duration for duration in throttle_durations
  18. if duration is not None
  19. ]
  20. duration = max(durations, default=None)
  21. self.throttled(request, duration)
  1. # 点进去来到这里,我们再去api_settings看一看
  2. class APIView(View):
  3. ...
  4. throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES
  5. ...
  1. 'DEFAULT_PERMISSION_CLASSES': [], # 默认配置为空,即不作任何频率限制

2. drf / throttling.py  源码

# 分析 第一部分

这里面有BaseThrottle和SimpleRateThrottle,其他类继承他们两个

BaseThrottle

  1. class BaseThrottle:
  2. # 判断是否限次:没有限次可以请求True,限次了不可以请求False
  3. def allow_request(self, request, view):
  4. raise NotImplementedError('.allow_request() must be overridden')
  5.  
  6. def get_ident(self, request):
  7. xff = request.META.get('HTTP_X_FORWARDED_FOR')
  8. remote_addr = request.META.get('REMOTE_ADDR')
  9. num_proxies = api_settings.NUM_PROXIES # 这个可以看到默认为空
  10.  
  11. # 上面一大堆默认为空,这个一定不会走,除非去修改默认配置,我们不知道上面一堆是什么先不用管
  12. if num_proxies is not None:
  13. if num_proxies == 0 or xff is None:
  14. return remote_addr
  15. addrs = xff.split(',')
  16. client_addr = addrs[-min(num_proxies, len(addrs))]
  17. return client_addr.strip()
  18.  
  19. return ''.join(xff.split()) if xff else remote_addr
  20.  
  21. # 限次后调用,显示还需等待多长时间才能再访问,返回等待的时间seconds
  22. def wait(self):
  23. return None

这里面代码太少,我们一脸懵逼,看来没找对,来看看SimpleRateThrottle的吧

SimpleRateThrottle

看完这个源码我们发现里面有这么多方法,一一看完也不知啥意思,还是无从下手,怎么办?

往下看,我们发现这三个表:

点开用户频率限制可以看到他们三个限制都设置了一个scope变量,都重写了get_cache_key方法 (自定义就仿照这个)

点开一个登录用户的认证:

  1. class UserRateThrottle(SimpleRateThrottle):
  2. scope = 'user'
  3. # 返回一个字符串
  4. def get_cache_key(self, request, view):
  5. if request.user.is_authenticated:
  6. ident = request.user.pk # 就是登录用户的pk
  7. else:
  8. ident = self.get_ident(request)
  9.  
  10. # 'throttle_%(scope)s_%(ident)s' 有名占位 —— 'throttle_user_pk'
  11. return self.cache_format % {
  12. 'scope': self.scope,
  13. 'ident': ident
  14. }

登录用户频率限制

我们考虑,要想走SimpleRateThrottle类,第一步肯定走init:

得到self.rate为None,继续往下走进入parse_rate

补充知识: django缓存

  1. django缓存
  2. # 1)导包: from django.core.cache import cache
  3. # 2) 添加缓存: cache.set(key, value, exp
  4. # 3) 获取缓存: cache.get(key, default_value)

# 分析 第二部分

我们回到入口文件:

# 分析 第三部分

  1. # 看完上面的BaseThrottle,里面代码太少,我们一脸懵逼,来看看这个简单的吧
  2. class SimpleRateThrottle(BaseThrottle):
  3. """
  4. A simple cache implementation, that only requires `.get_cache_key()`
  5. to be overridden.
  6.  
  7. The rate (requests / seconds) is set by a `rate` attribute on the View
  8. class. The attribute is a string of the form 'number_of_requests/period'.
  9.  
  10. Period should be one of: ('s', 'sec', 'm', 'min', 'h', 'hour', 'd', 'day')
  11.  
  12. Previous request information used for throttling is stored in the cache.
  13. """
  14. cache = default_cache
  15. timer = time.time
  16. cache_format = 'throttle_%(scope)s_%(ident)s'
  17. scope = None
  18. THROTTLE_RATES = api_settings.DEFAULT_THROTTLE_RATES
  19.  
  20. def __init__(self):
  21. if not getattr(self, 'rate', None):
  22. self.rate = self.get_rate() # None
  23. self.num_requests, self.duration = self.parse_rate(self.rate)
  24. # 从配置文件DEFAULT_THROTTLE_RATES中根据scope得到频率配置(次数/时间 3/min)
  25. # scope 作为频率认证类的类属性,写死
  26. # 将频率配置解析成次数和时间分别存放到self.num_ requests, self.duration中
  27. def get_cache_key(self, request, view):
  28. """
  29. Should return a unique cache-key which can be used for throttling.
  30. Must be overridden.
  31.  
  32. May return `None` if the request should not be throttled.
  33. """
  34. raise NotImplementedError('.get_cache_key() must be overridden') # 提示必须被重写,重写的在下方用户类中
  35.  
  36. def get_rate(self):
  37. """
  38. Determine the string representation of the allowed request rate.
  39. """
  40. # 没有scope直接抛异常,我们有并且就是user,往下看
  41. if not getattr(self, 'scope', None):
  42. msg = ("You must set either `.scope` or `.rate` for '%s' throttle" %
  43. self.__class__.__name__)
  44. raise ImproperlyConfigured(msg)
  45.  
  46. try:
  47. # scope:‘user’,所以有值,字典取值不会抛异常,也就是返回none,然后我们再去init中
  48. return self.THROTTLE_RATES[self.scope]
  49. # 频率限制条件配置,需要拿到自己的配置中去
  50. # 'DEFAULT_THROTTLE_RATES': {
  51. # 'user': None,
  52. # 'anon': None,},
  53. except KeyError:
  54. # 这是scope无值报的异常,提示你去设置scope
  55. msg = "No default throttle rate set for '%s' scope" % self.scope
  56. raise ImproperlyConfigured(msg)
  57.  
  58. def parse_rate(self, rate):
  59. """
  60. Given the request rate string, return a two tuple of:
  61. <allowed number of requests>, <period of time in seconds>
  62. """
  63. if rate is None:
  64. return (None, None)
  65. # 返回两个None,也就是说init中self.num_requests, self.duration都为None,
  66. # 显然不是我们想要的结果,我们不妨继续往下看看
  67. # rate如果有值肯定是字符串, int/s、m、h、d开头的字符串
  68. # 到这里我们就明白了,我们必须得让rate有值,也就是需要去DEFAULT_THROTTLE_RATES中修改或设置,
  69. # 我们假设修改 user:3/min ,重新返回init中去看一下
  70. num, period = rate.split('/')
  71. num_requests = int(num)
  72. duration = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}[period[0]]
  73. return (num_requests, duration)
  74.  
  75. def allow_request(self, request, view):
  76. """
  77. Implement the check to see if the request should be throttled.
  78.  
  79. On success calls `throttle_success`.
  80. On failure calls `throttle_failure`.
  81. """
  82. if self.rate is None:
  83. return True
  84.  
  85. self.key = self.get_cache_key(request, view)
  86. # get_cache_key在下方用户类中被重写 返回值为 'throttle_user_2'
  87. if self.key is None:
  88. return True
  89. # django缓存
  90. # 1)导包: from django.core.cache import cache
  91. # 2) 添加缓存: cache.set(key, value, exp
  92. # 3) 获取缓存: cache.get(key, default_value)
  93.  
  94. # 初次访问缓存为空,history为[]
  95. self.history = self.cache.get(self.key, []) # self.key = throttle_user_2
  96. # 顶部 获取当前时间 timer = time.time
  97. self.now = self.timer()
  98. # throttle duration
  99. # 第一次访问,不会进来
  100. # 第二次访问就会走,意思是最早一次访问时间和此时是否大于限制时间一分钟,如果大于就弹出最早一次的时间,相当于次数减一
  101. while self.history and self.history[-1] <= self.now - self.duration:
  102. self.history.pop()
  103. # history的长度和限制次数3作比较,超限访问throttle_failure,直接返回False
  104. if len(self.history) >= self.num_requests: # self.num_requests为我们设置的 3/min 的3
  105. return self.throttle_failure()
  106. # 访问次数未达到限制次数,返回可以访问,接着看throttle_success
  107. return self.throttle_success()
  108.  
  109. def throttle_success(self):
  110. """
  111. Inserts the current request's timestamp along with the key
  112. into the cache.
  113. """
  114. # 在history列表最前面插入当前时间
  115. self.history.insert(0, self.now)
  116. # 在缓存中添加self.key, self.history(访问时间列表), self.duration(min)
  117. self.cache.set(self.key, self.history, self.duration)
  118. return True
  119.  
  120. def throttle_failure(self):
  121. """
  122. Called when a request to the API has failed due to throttling.
  123. """
  124. return False
  125.  
  126. def wait(self):
  127. """
  128. Returns the recommended next request time in seconds.
  129. """
  130. if self.history:
  131. # 就是比较min即60s和初次访问到现在时间间隔的大小
  132. remaining_duration = self.duration - (self.now - self.history[-1])
  133. else:
  134. remaining_duration = self.duration
  135.  
  136. # 求还可以访问多少次 次数= 最多次数 - 已经访问的次数
  137. available_requests = self.num_requests - len(self.history) + 1
  138. # 不能访问返回 None
  139. if available_requests <= 0:
  140. return None
  141.  
  142. return remaining_duration / float(available_requests)
  143.  
  144. class AnonRateThrottle(SimpleRateThrottle):
  145. """
  146. Limits the rate of API calls that may be made by a anonymous users.
  147.  
  148. The IP address of the request will be used as the unique cache key.
  149. """
  150. scope = 'anon'
  151.  
  152. def get_cache_key(self, request, view):
  153. if request.user.is_authenticated:
  154. return None # Only throttle unauthenticated requests.
  155.  
  156. return self.cache_format % {
  157. 'scope': self.scope,
  158. 'ident': self.get_ident(request)
  159. }
  160.  
  161. class UserRateThrottle(SimpleRateThrottle):
  162. """
  163. Limits the rate of API calls that may be made by a given user.
  164.  
  165. The user id will be used as a unique cache key if the user is
  166. authenticated. For anonymous requests, the IP address of the request will
  167. be used.
  168. """
  169. scope = 'user'
  170. # 返回一个字符串
  171. def get_cache_key(self, request, view):
  172. if request.user.is_authenticated:
  173. ident = request.user.pk # 就是登录用户的pk
  174. else:
  175. ident = self.get_ident(request)
  176.  
  177. # 'throttle_%(scope)s_%(ident)s' 有名占位 —— 'throttle_user_pk'
  178. return self.cache_format % {
  179. 'scope': self.scope,
  180. 'ident': ident
  181. }
  182.  
  183. class ScopedRateThrottle(SimpleRateThrottle):
  184. """
  185. Limits the rate of API calls by different amounts for various parts of
  186. the API. Any view that has the `throttle_scope` property set will be
  187. throttled. The unique cache key will be generated by concatenating the
  188. user id of the request, and the scope of the view being accessed.
  189. """
  190. scope_attr = 'throttle_scope'
  191.  
  192. def __init__(self):
  193. # Override the usual SimpleRateThrottle, because we can't determine
  194. # the rate until called by the view.
  195. pass
  196.  
  197. def allow_request(self, request, view):
  198. # We can only determine the scope once we're called by the view.
  199. self.scope = getattr(view, self.scope_attr, None)
  200.  
  201. # If a view does not have a `throttle_scope` always allow the request
  202. if not self.scope:
  203. return True
  204.  
  205. # Determine the allowed request rate as we normally would during
  206. # the `__init__` call.
  207. self.rate = self.get_rate()
  208. self.num_requests, self.duration = self.parse_rate(self.rate)
  209.  
  210. # We can now proceed as normal.
  211. return super().allow_request(request, view)
  212.  
  213. def get_cache_key(self, request, view):
  214. """
  215. If `view.throttle_scope` is not set, don't apply this throttle.
  216.  
  217. Otherwise generate the unique cache key by concatenating the user id
  218. with the '.throttle_scope` property of the view.
  219. """
  220. if request.user.is_authenticated:
  221. ident = request.user.pk
  222. else:
  223. ident = self.get_ident(request)
  224.  
  225. return self.cache_format % {
  226. 'scope': self.scope,
  227. 'ident': ident
  228. }

整个源码

3.自定义频率类

  1. # 1) 自定义一个继承 SimpleRateThrottle 类 的频率类
    # 2) 设置一个 scope 类属性,属性值为任意见名知意的字符串
    # 3) 在settings配置文件中,配置drf的DEFAULT_THROTTLE_RATES,格式为 {scope字符串: '次数/时间'}
    # 4) 在自定义频率类中重写 get_cache_key 方法
    # 限制的对象返回 与限制信息有关的字符串
       # 不限制的对象返回 None (只能返回None,不能是False或是''等)
  1. 自定义频率组件:
  2. class MyThrottle(SimpleRateThrottle):
  3. scope = 'sms'
  4. def get_cache_key(self, request, view):
  5. # 从request的 query_params、data、META 及 view 中 获取限制的条件
  6. return '与认证信息有关的动态字符串'
  7.  
  8. settings文件中要有scope对应的rate配置 {'sms': '3/min'}

示例: 短信接口 1/min 频率限制

频率:api/throttles.py
  1. from rest_framework.throttling import SimpleRateThrottle

  2. class SMSRateThrottle(SimpleRateThrottle):
  3. scope = 'sms'
  4. # 只对提交手机号的get方法(param携带参数)进行限制,因为是query_params里面取的数据
  5. # drf请求的所有url拼接参数(param携带)均被解析到query_params中,所有数据包数据(body携带)都被解析到data中
  6. def get_cache_key(self, request, view):
  7. mobile = request.query_params.get('mobile')
  8. # 没有手机号,就不做频率限制
  9. if not mobile:
  10. return None
  11. # 返回可以根据手机号动态变化,且不易重复的字符串,作为操作缓存的key
  12. return 'throttle_%(scope)s_%(ident)s' % {'scope': self.scope, 'ident': mobile}
配置:settings.py
  1. # drf配置
  2. REST_FRAMEWORK = {
  3. # 频率限制条件配置
  4. 'DEFAULT_THROTTLE_RATES': {
  5. 'sms': '1/min'
  6. },
  7. }
视图:views.py
  1. from .throttles import SMSRateThrottle
  2. class TestSMSAPIView(APIView):
  3. # 局部配置频率认证
  4. throttle_classes = [SMSRateThrottle]
  5. def get(self, request, *args, **kwargs):
  6. return APIResponse(0, 'get 获取验证码 OK')
  7. def post(self, request, *args, **kwargs):
  8. return APIResponse(0, 'post 获取验证码 OK')
路由:api/url.py
  1. url(r'^sms/$', views.TestSMSAPIView.as_view()),
会受限制的接口
  1. # 只会对 /api/sms/?mobile=具体手机号 接口才会有频率限制
  2. # 1)对 /api/sms/ 或其他接口发送无限制
  3. # 2)对数据包提交mobile的/api/sms/接口无限制
  4. # 3)对不是mobile(如phone)字段提交的电话接口无限制

8) drf 三大认证 认证 权限 频率的更多相关文章

  1. drf框架 - 三大认证组件 | 认证组件 | 权限组件 | 频率组件

    RBAC 基于用户权限访问控制的认证 - Role-Based Access Control Django框架采用的是RBAC认证规则,RBAC认证规则通常会分为 三表规则.五表规则,Django采用 ...

  2. drf三大组件之认证组件与权限组件

    复习 """ 视图家族 1.视图类:APIView.GenericAPIView APIView:作为drf的基础view:as_view()禁用csrf:dispatc ...

  3. drf三大认证:认证组件-权限组件-权限六表-自定义认证组件的使用

    三大认证工作原理简介 认证.权限.频率 源码分析: from rest_framework.views import APIView 源码分析入口: 内部的三大认证方法封装: 三大组件的原理分析: 权 ...

  4. 实战-DRF快速写接口(认证权限频率)

    实战-DRF快速写接口 开发环境 Python3.6 Pycharm专业版2021.2.3 Sqlite3 Django 2.2 djangorestframework3.13 测试工具 Postma ...

  5. DRF 三大认证的配置及使用方法

    目录 三大认证 一.身份认证 1.身份认证配置 1.1 全局配置身份认证模块 1.2 局部配置身份认证模块 2.drf提供的身份认证类(了解) 3.rf-jwt提供的身份认证类(常用) 4.自定义身份 ...

  6. DRF的版本、认证、权限

    DRF的版本 版本控制是做什么用的, 我们为什么要用 首先我们要知道我们的版本是干嘛用的呢~~大家都知道我们开发项目是有多个版本的~~ 当我们项目越来越更新~版本就越来越多~~我们不可能新的版本出了~ ...

  7. DRF(4) - 认证、权限组件

    一.引入 通过前面三节课的学习,我们已经详细了解了DRF提供的几个重要的工具,DRF充分利用了面向对象编程的思想,对Django的View类进行了继承,并封装了其as_view方法和dispatch方 ...

  8. DRF的认证、权限 和 限制

    一.概述 认证是将传入请求与一组标识凭据(例如请求来自的用户或其签名的令牌)相关联的机制.然后 权限 和 限制 组件决定是否拒绝这个请求. 简单来说就是: 认证确定了你是谁 权限确定你能不能访问某个接 ...

  9. rest-framework框架——认证、权限、频率组件

    一.rest-framework登录验证 1.models.py添加User和Token模型 class User(models.Model): name = models.CharField(max ...

随机推荐

  1. 单线程IP扫描解析

    扫描代码: private void Button_Click(object sender, RoutedEventArgs e) { a5.Items.Clear(); string str = t ...

  2. Python 0(安装及初步使用+学习资源推荐)

    不足之处,还请见谅,请指出不足.本人发布过的文章,会不断更改,力求减少错误信息. Python安装请借鉴网址https://www.runoob.com/python/python-install.h ...

  3. Linux 下发送邮件

    由于种种原因,需要由我这个兼职运维每天发送对账单文件给运营同学,故研究下 Linux 发送邮件,希望对大家有所帮助. 安装 # Centos,安装 mailx $ yum install -y mai ...

  4. E - Sum of gcd of Tuples (Hard) Atcoder 162 E(容斥)

    题解:这个题目看着挺吓人的,如果仔细想想的话,应该能想出来.题解还是挺好的理解的. 首先设gcd(a1,a2,a3...an)=i,那么a1~an一定是i的倍数,所以ai一共有k/i种取值.有n个数, ...

  5. 忍不住还是手写了一遍博客的css

    F12边调边改,的一点一点撸出来这个效果.感觉已经可以了.日历感觉没什么用直接隐藏了.

  6. python爬虫实例,一小时上手爬取淘宝评论(附代码)

    前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 1 明确目的 通过访问天猫的网站,先搜索对应的商品,然后爬取它的评论数据. ...

  7. php+mysql数据库联合查询 left join 右侧数据重复问题

    情况:多表联合查询(三表及以上联合查询) 分析: A left join B left join C left join D 假如: 表B.C.D都与表A关联查询 A left join B 4条数据 ...

  8. 3. JS生成32位随机数

    function randomWord ( randomFlag,min,max ) { var str = " ", range = min, arr = ['0','1','2 ...

  9. 数据源管理 | PostgreSQL环境整合,JSON类型应用

    本文源码:GitHub·点这里 || GitEE·点这里 一.PostgreSQL简介 1.和MySQL的比较 PostgreSQL是一个功能强大的且开源关系型数据库系统,在网上PostgreSQL和 ...

  10. gojs去水印

    重点在go.js文件中将这个方法中的代码注释掉即可 搜索关键字 7ca11abfd022028846 然后注释下列代码,注释前先整理JS格式 function ni() { if(th) { var ...