zoukankan      html  css  js  c++  java
  • drf频率源码、自动生成接口文档、JWT

    一、drf频率源码分析

    from rest_framework.throttling import SimpleRateThrottle
    
    
    # 在频率限制中最重要的方法就是allow_request
    # 可以直接去频率类的顶级父类里找,告诉我们如果要写频率限制,就必须重写这个方法,返回值True/False
    # wait返回的数字是在前端展示的还差的时间
    # 获取rate的方法
     def __init__(self):
            if not getattr(self, 'rate', None):
                self.rate = self.get_rate()
            self.num_requests, self.duration = self.parse_rate(self.rate)
    # 所以在分析内置的SimpleRateThrottle频率限制模块的时候就看这个方法········
        def allow_request(self, request, view):
    		# 根据配置文件的DEFAULT_THROTTLE_RATES中的scope获取频率配置
            # scope不是写死的,如果我们在继承的类属性中写了scope=‘xxx’那么去配置信息找的时候就是xxx:3/m
            # 我们配置的3/m
            # 将这个字符串分成次数和时间,分别存放到self.num_requests和self.duration中
            if self.rate is None:
                return True
    		# 调用get_cache_key方法,这个类中没有写这个方法
            # 也就是我们要使用这个类就必须调用这个方法
            # 这个方法的返回值就是我们限制频率的约束如ip,username,如果返回None就不限制
            self.key = self.get_cache_key(request, view)
            if self.key is None:
                return True
    		# 把访问信息添加到缓存当中
            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()
            if len(self.history) >= self.num_requests:
                # 列表长度如果超出我们限定的就返回错误
                return self.throttle_failure()
            # 如果没超出就返回True并把列表保存到缓存中
            return self.throttle_success()
    
    

    二、自动生成接口文档

    REST framework可以自动帮助我们生成接口文档。

    接口文档以网页的方式呈现。

    自动接口文档能生成的是继承自APIView及其子类的视图。

    1 安装依赖

    REST framewrok生成接口文档需要coreapi库的支持。

    pip install coreapi
    

    2 设置接口文档访问路径

    在总路由中添加接口文档路径。

    文档路由对应的视图配置为rest_framework.documentation.include_docs_urls

    参数title为接口文档网站的标题。

    from rest_framework.documentation import include_docs_urls
    
    urlpatterns = [
        ...
        path('docs/', include_docs_urls(title='站点页面标题'))
    ]
    

    3 文档描述说明的定义位置

    1) 单一方法的视图,可直接使用类视图的文档字符串,如

    class BookListView(generics.ListAPIView):
        """
        返回所有图书信息.
        """
    

    2)包含多个方法的视图,在类视图的文档字符串中,分开方法定义,如

    class BookListCreateView(generics.ListCreateAPIView):
        """
        get:
        返回所有图书信息.
    
        post:
        新建图书.
        """
    

    3)对于视图集ViewSet,仍在类视图的文档字符串中封开定义,但是应使用action名称区分,如

    class BookInfoViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, GenericViewSet):
        """
        list:
        返回图书列表数据
    
        retrieve:
        返回图书详情数据
    
        latest:
        返回最新的图书数据
    
        read:
        修改图书的阅读量
        """
    

    4 访问接口文档网页

    浏览器访问 127.0.0.1:8000/docs/,即可看到自动生成的接口文档。

    image-20200714155134704

    三、JWT

    1 JWT基本原理

    参考:http://liuqingzheng.top/python/Django-rest-framework框架/9-drf-JWT认证/

    JWT = Json Web Token,本质上就是token

    分布式和集群

    分布式是指通过网络连接的多个组件,通过交换信息协作而形成的系统。而集群,是指同一种组件的多个实例,形成的逻辑上的整体。

    简单来说集群就是,项目所用的所有功能,前后端,数据库,缓存都放在一个机器上,然后多放几个这样的机器,上面的项目都是一样的

    分布式就是我把一个项目用的东西分别放在不同的机器上,a机器装数据库,b机器放后端代码等等

    所以集群相对于分布式有负载均衡的效果,一台机子宕机了,这个站点也能继续被访问,而分布式一处死了,就完全没法继续了

    jwt的应用场景:当我们用分布式的项目的时候,如果是统一的用一个数据库,那没问题,如果是各个机器用自己的数据库,那如果我在a机器上注册账号,登录了,下次如果访问的是b服务器就又得重新登录,因为我的登录状态不在这个机子上。

    jwt认证可以让客户端发送的数据有服务端的特性,检验这种特性来判断是否是这个服务端的数据,再根据特定的规则解码得到我们需要的数据。

    jwt数据构成

    JWT就是一段字符串,由三段信息构成的,将这三段信息文本用.链接一起就构成了Jwt字符串。就像这样:

    eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
    

    第一部分我们称它为头部(header),第二部分我们称其为载荷(payload, 类似于飞机上承载的物品),第三部分是签证(signature).

    1.1 header

    jwt的头部承载两部分信息:

    • 声明类型,这里是jwt
    • 声明加密的算法 通常直接使用 HMAC SHA256

    完整的头部就像下面这样的JSON:

    {
      'typ': 'JWT',
      'alg': 'HS256'
    }
    

    然后将头部进行base64加密(该加密是可以对称解密的),构成了第一部分.

    eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
    

    1.2 payload

    载荷就是存放有效信息的地方。这个名字像是特指飞机上承载的货品,这些有效信息包含三个部分

    • 标准中注册的声明
    • 公共的声明
    • 私有的声明

    标准中注册的声明 (建议但不强制使用) :

    • iss: jwt签发者
    • sub: jwt所面向的用户
    • aud: 接收jwt的一方
    • exp: jwt的过期时间,这个过期时间必须要大于签发时间
    • nbf: 定义在什么时间之前,该jwt都是不可用的.
    • iat: jwt的签发时间
    • jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避时序攻击。

    公共的声明 : 公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密.

    私有的声明 : 私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息。

    定义一个payload:

    {
      "sub": "1234567890",
      "name": "John Doe",
      "admin": true
    }
    

    然后将其进行base64加密,得到JWT的第二部分。

    eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9
    

    1.3 signature

    JWT的第三部分是一个签证信息,这个签证信息由三部分组成:

    • header (base64后的)
    • payload (base64后的)
    • secret

    这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分。

    // javascript
    var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload);
    
    var signature = HMACSHA256(encodedString, 'secret'); // TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
    

    将这三部分用.连接成一个完整的字符串,构成了最终的jwt:

    eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
    

    注意:secret是保存在服务器端的,jwt的签发生成也是在服务器端的,secret就是用来进行jwt的签发和jwt的验证,所以,它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。

    关于签发和核验JWT,我们可以使用Django REST framework JWT扩展来完成。

    1.4 认证算法:签发与校验

    """
    1)jwt分三段式:头.体.签名 (head.payload.sgin)
    2)头和体是可逆加密,让服务器可以反解出user对象;签名是不可逆加密,保证整个token的安全性的
    3)头体签名三部分,都是采用json格式的字符串,进行加密,可逆加密一般采用base64算法,不可逆加密一般采用hash(md5)算法
    4)头中的内容是基本信息:公司信息、项目组信息、token采用的加密方式信息
    {
    	"company": "公司信息",
    	...
    }
    5)体中的内容是关键信息:用户主键、用户名、签发时客户端信息(设备号、地址)、过期时间
    {
    	"user_id": 1,
    	...
    }
    6)签名中的内容时安全信息:头的加密结果 + 体的加密结果 + 服务器不对外公开的安全码 进行md5加密
    {
    	"head": "头的加密字符串",
    	"payload": "体的加密字符串",
    	"secret_key": "安全码"
    }
    """
    

    签发:根据登录请求提交来的 账号 + 密码 + 设备信息 签发 token

    """
    1)用基本信息存储json字典,采用base64算法加密得到 头字符串
    2)用关键信息存储json字典,采用base64算法加密得到 体字符串
    3)用头、体加密字符串再加安全码信息存储json字典,采用hash md5算法加密得到 签名字符串
    
    账号密码就能根据User表得到user对象,形成的三段字符串用 . 拼接成token返回给前台
    """
    

    校验:根据客户端带token的请求 反解出 user 对象

    """
    1)将token按 . 拆分为三段字符串,第一段 头加密字符串 一般不需要做任何处理
    2)第二段 体加密字符串,要反解出用户主键,通过主键从User表中就能得到登录用户,过期时间和设备信息都是安全信息,确保token没过期,且时同一设备来的
    3)再用 第一段 + 第二段 + 服务器安全码 不可逆md5加密,与第三段 签名字符串 进行碰撞校验,通过后才能代表第二段校验得到的user对象就是合法的登录用户
    """
    

    2 jwt的基本使用

    安装:pip install djangorestframework-jwt

    # 路由配置
    from django.urls import path
    from rest_framework_jwt.views import obtain_jwt_token
    urlpatterns = [
        path('login/', obtain_jwt_token),
    ]
    # settings注册app
    # 在postman发送JSON格式的注册的用户账号和密码
    {
        "username": "hz",
        "password":"hz123456"
    }
    # 返回的jwt数据
    {
        "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6Imh6IiwiZXhwIjoxNTk0NzE1MTEyLCJlbWFpbCI6IiJ9.3Qos0Ua0hZfK2Nn3P0jR6yxNSrBeloIVplbUBZILUpM"
    }
    # 在我们要访问设置的认证配置的url时,要注意必须在请求头中设置
    Authorization : JWT eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6Imh6IiwiZXhwIjoxNTk0NzE1MTEyLCJlbWFpbCI6IiJ9.3Qos0Ua0hZfK2Nn3P0jR6yxNSrBeloIVplbUBZILUpM"
    # 注意这里是JWT空格+token信息,这是jwt认证写死的,必须按照这个规范来
    

    3 自定制auth认证类

    from rest_framework_jwt.authentication import BaseJSONWebTokenAuthentication
    # 重写父类的authenticate即可
    class MyToekn(BaseJSONWebTokenAuthentication):
        def authenticate(self, request):
            jwt_value = get_authorization_header(request).split()[1]
            # jwt_value = str(request.META.get('HTTP_AUTHORIZATION'))
    
            if not jwt_value:
                raise AuthenticationFailed('Authorization 字段是必须的')
            try:
                payload = jwt_decode_handler(jwt_value)
            except Exception:
                raise AuthenticationFailed('认证失败')
    
            user = self.authenticate_credentials(payload)
    
            return user, jwt_value
    
    
  • 相关阅读:
    Django Rest Framework 教程及API向导
    zabbix2.4升级到2.5 --考虑升级到zabbix3.0
    followme_laser包解读
    ROS多个工作空间存在同名包的BUG
    fatal err Eigen/Dense No such file or directory(unsupported/Eigen/FFT、Eigen/Core也是一样的道理)
    ROS向节点传递参数的方法总结(rosrun,launch) + (参数服务器,main函数参数)
    同步Sublime Text配置
    W: Failed to fetch http://packages.microsoft.com/repos/vscode/dists/stable/main/binary-amd64/Package
    Ubuntu(Linux)下更新CMake,最安全的更新
    Ignoring Provides line with DepCompareOp for package gdb-minimal
  • 原文地址:https://www.cnblogs.com/hz2lxt/p/13300549.html
Copyright © 2011-2022 走看看