• day 091 DRF -- 版本 | 认证 | 权限 | 限制


    主要内容:

    • 1.版本控制
    • 2.认证
    • 3.权限
    • 4.限制

    1.版本控制

    1.1 版本控制的缘由

    • API 版本控制允许我们在不同的客户端之间更改行为(同一个接口的不同版本会返回不同的数据)。 DRF提供了许多不同的版本控制方案。
    • 可能会有一些客户端因为某些原因不再维护了,但是我们后端的接口还要不断的更新迭代,这个时候通过版本控制返回不同的内容就是一种不错的解决方案

    1.2 DRF - 版本控制方案

           

    1.3 版本控制系统的使用

    • (1)在settings文件中的配置(全局配置)
      #setting是文件中DRF的配置
      REST_FRAMEWORK = {
          ...
          'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning',
          'DEFAULT_VERSION': 'v1',  # 默认的版本
          'ALLOWED_VERSIONS': ['v1', 'v2'],  # 有效的版本
          'VERSION_PARAM': 'version',  # 版本的参数名与URL conf中一致
      }
    • (2) 路由
      urlpatterns = [
          ...
          url(r'^(?P<version>[v1|v2]+)/publishers/$', views.PublisherViewSet.as_view({'get': 'list', 'post': 'create'})),
          url(r'^(?P<version>[v1|v2]+)/publishers/(?P<pk>d+)/$', views.PublisherViewSet.as_view({'get': 'retrieve', 'put': 'update', 'delete': 'destroy'})),
      
      ]
    • (3)视图
      #不同版本使用不同的序列化类
      class PublisherViewSet(ModelViewSet):
      
          def get_serializer_class(self):
              """不同的版本使用不同的序列化类"""
              if self.request.version == 'v1':
                  return PublisherModelSerializerVersion1
              else:
                  return PublisherModelSerializer
          queryset = models.Publisher.objects.all()

    注:局部配置:

    # 可以在视图中设置versioning_class属性,如下:
    class
    PublisherListView(ListCreateAPIView): queryset = models.Publisher.objects.all() serializer_class = PublisherModelSerializer def get_queryset(self): if self.request.version == 'v1': return models.Publisher.objects.all()[:2] else: return self.queryset.all()

    2. 认证  

    2.1 概述

    身份验证是将传入请求与一组标识凭据(例如请求来自的用户或其签名的令牌)相关联的机制。然后 权限 和 限制 组件决定是否拒绝这个请求。

    简单来说就是:

    • 认证确定了你是谁
    • 权限确定你能不能访问某个接口
    • 限制确定你访问某个接口的频率

    REST framework 提供了一些开箱即用的身份验证方案,并且还允许你实现自定义方案。

                            

    2.2基于Token的认证方案

    • (1) model设计
      class UserInfo(models.Model):
          name = models.CharField(max_length=32)
          password = models.CharField(max_length=32)
          vip =models.BooleanField(default=False)
          token = models.CharField(max_length=128,blank=True,null=True)
    • (2) url
      #此处是又创建了APP在根路由通过include实现路由的分发
      urlpatterns = [
          url(r'^reg/$', views.RegView.as_view()),
          url(r'^login/$', views.LoginView.as_view()),
          url(r'^test_auth/$', views.TestAuthView.as_view()),  #测试登录认证
      ]
    • (3)认证类
      from rest_framework.authentication import BaseAuthentication
      from rest_framework.exceptions import AuthenticationFailed
      from BAR import models
      
      class MyAuth(BaseAuthentication):
          #重写authenticate方法
          def authenticate(self, request):
              # 必须返回元组,或者抛出 AuthenticationFailed 异常
              token = request.query_params.get('token')
              if token:
                  #如果请求的URL中携带了token参数
                  user_obj = models.UserInfo.objects.filter(token=token).first()
                  if user_obj:
                      #如果token是有效的
                      return user_obj,token
                  else:
                      raise  AuthenticationFailed('无效的token')
              else:
                  raise AuthenticationFailed('请求的URL必须携带token参数')
    • (4)视图
      from rest_framework.views import APIView
      from BAR import models
      from rest_framework.response import Response
      import uuid
      
      class RegView(APIView):
          '''
          注册用户类
          '''
          def post(self,request):
              name = request.data.get('name')
              pwd = request.data.get('password')
              re_pwd = request.data.get('password')
              if name and pwd:
                  if re_pwd == pwd:
                      models.UserInfo.objects.create(name=name,password=pwd)
                      return Response('账号注册成功')
                  else:
                      raise Response('两次输入的密码不一致')
              else:
                  return Response('无效的参数')
      
      class LoginView(APIView):
          def post(self,request):
              name = request.data.get('name')
              pwd = request.data.get('password')
              if name and pwd:
                  user_obj = models.UserInfo.objects.filter(name=name,password=pwd).first()
                  if user_obj:
                      #登录成功
                      #生成token(事件戳+mac地址)
                      token = uuid.uuid1().hex
                      #保存在用户表中
                      user_obj.token = token
                      user_obj.save()
                      #给用户返回
                      return Response ({'error_no':0,'token':token})
                  else:
                      #用户名或者密码错误
                      return Response ({'error_no':1,'error':'用户名或密码错误'})
              else:
                  return Response('无效的参数')
      
    • 局部配置
      from BAR.auth import  MyAuth
      
      class TestAuthView(APIView):
          # 视图级别的认证
          authentication_classes = [MyAuth, ]
      
          def get(self,request):
              return Response('这个视图里面的数据只有登录后才能看到')
    • 也可以在全局配置
      #在settings文件中
      REST_FRAMEWORK = {
          ...
          'DEFAULT_AUTHENTICATION_CLASSES': ['auth_demo.auth.MyAuth', ]
          #列表 -- 可以是多个
      }

    : 一般都是全局配置,极个别情况下会给某个视图配置   局部的配置优先级高于全局配置

    另外:authenticate方法返回值: 返回元组,元组的第一个元素赋值给 request.user 第二个元素复制给了request.auth

    2.3 authenticate方法 抛错后:

     

    注;从上述可以得出当 捕获到报错(raise),此时执行的  _not_authenticated  方法的return 结果 : user &auth 都赋值为None 

    3.权限

    自定义一个权限类 (只有VIP用户才能看的内容)

    • 3.1自定义权限类
      from rest_framework.permissions import BasePermission
      
      class MyPermission(BasePermission):
          message = '只有VIP才能访问'
      
          def has_permission(self, request, view):
      #通过上面的认证源码得知:当不输入token参数或者未登录,则 user ,auth 均为None,当auth存在则此时的user不为None
      if not request.auth: return False #当有Vip才有权限访问 #if request.user 当前经过认证的用户对象 if request.user.vip: return True else: #如果不是Vip就拒绝的范围 return False
    • 3.2 视图
      from BAR.auth import  MyAuth
      from BAR.permissions import MyPermission
      
      class TestAuthView(APIView):
          authentication_classes = [MyAuth, ]
          permission_classes = [MyPermission, ]
      
          def get(self,request):
              return Response('这个视图里面的数据只有登录后才能看到')

    注:可以全局范围配置

    REST_FRAMEWORK = {
        "DEFAULT_AUTHENTICATION_CLASSES": ["BAR.auth.MyAuth", ],
        "DEFAULT_PERMISSION_CLASSES": ["BAR.permissions.MyPermission", ]
    }

    关于报错; 

     'AnonymousUser' object has no attribute 'vip'   (匿名用户没有VIP权限,)

     原因:由于permissions.py中没有判断是否通过认证(即request.auth是否存在)

    4.限制

    4.1 自定义限制类

    • (1) throttle.py
      import time
      visit_record ={}
      class MyThrottle(object):
          def __init__(self):
              self.history = None
      
          def allow_request(self,request,view):
              print(request.META)
              #拿到当前的请求的ip作为访问记录的key
              ip = request.META.get('REMOTE_ADDR')
              now = time.time()
              if ip not in visit_record:
                  visit_record[ip] = []
              #把当前的请求的访问记录拿出来保存到一个变量中
              history = visit_record[ip]
              self.history = history
              #循环访问历史,把超过10 秒钟的请求事件去掉
              while history and now - history[-1] >10:
                  history.pop()
              if len(history) >=3:
                  return False
              else:
                  self.history.insert(0,now)
                  return True
      
          def wait(self):
              now = time.time()
              return self.history[-1] +10 -now
    • (2) 视图
      from BAR.XXX import MyThrottle
      
      class TestAuthView(APIView):
          # authentication_classes = [MyAuth, ]
          # permission_classes = [MyPermission, ]
          throttle_classes = [MyThrottle, ]
      
          def get(self,request):
              return Response('这个视图里面的数据只有登录后才能看到')
    • 注, 全局使用
      #在settings文件中进行配置
      "DEFAULT_THROTTLE_CLASSES": ["BAR.throttle.MyThrottle", ],

    4.2 使用内置限制类

    • (1) throttle.py
      #使用内置限制类
      from rest_framework.throttling import SimpleRateThrottle
      
      class VisitThrottle(SimpleRateThrottle):
      
          scope = "xxx"
      
          def get_cache_key(self, request, view):
              return self.get_ident(request)
    • (2) 全局配置
         #在settings文件中进行配置
         "DEFAULT_THROTTLE_CLASSES": ["BAR.XXX.VisitThrottle", ],
          "DEFAULT_THROTTLE_RATES": {
              "xxx": "1/s",
  • 相关阅读:
    服务器基础知识
    web服务器与应用服务器
    QGraphics
    对话框Dialog
    信号和槽
    nginx 杂记
    go语言nsq源码解读八 http.go、http_server.go
    go语言nsq源码解读七 lookup_protocol_v1.go
    go语言nsq源码解读六 tcp.go、tcp_server.go
    go语言nsq源码解读五 nsqlookupd源码registration_db.go
  • 原文地址:https://www.cnblogs.com/wcx666/p/10274280.html
Copyright © 2020-2023  润新知