zoukankan      html  css  js  c++  java
  • day86-jwt-自定制频率、自动生成接口文档、JWT 自定制auth认证类

    昨日回顾

    #1  book 其实是5个表(自动生成了一个),
    	-一对一关系,其实是Forainkey,unique
        -on_delete:级联删除,设置为空,什么都不干,设置成默认值
        -字段建索引,字段唯一
        -联合索引,联合唯一
        -日期类型 auto_now  和 auto_now_add  
        -基表  abstract
    #2 book 	
    	-单条查询,多条查询
        -单条增,多条增(生成序列化对象,many=True-单条修改,多条修改(BookListSerializer:重写了update方法)
        -单删,群删(is_delete),统一用群删  pk__in=[1,2,3]
    # 3 频率
    	-自定义频率(ip,user_id)
        -继承SimpleRateThrottle
        -重写get_cache_key,返回什么就以什么为key进行限制
        -scope字段,需要与setting中对应
        
    #4 分页
    	-PageNumberPagination,基本分页
        	-每页显示大小
            -get请求路径中查询的key
            -get请求路径中每页显示条数
            -每页最大显示多少条
        -LimitOffsetPagination,
        	#     default_limit = 3   # 每页条数
            #     limit_query_param = 'limit' # 往后拿几条
            #     offset_query_param = 'offset' # 标杆
            #     max_limit = 5   # 每页最大几条
        -CursorPagination
            cursor_query_param = 'cursor'  # 每一页查询的key
            page_size = 2   #每页显示的条数
            ordering = '-id'  #排序字段-
    

    今日内容

    1 自定制频率

    # 自定制频率类,需要写两个方法
    	-# 判断是否限次:没有限次可以请求True,限次了不可以请求False
        	def allow_request(self, request, view):
        -# 限次后调用,显示还需等待多长时间才能再访问,返回等待的时间seconds
        	def wait(self):
                
    # 代码
    import time
    class IPThrottle():
        #定义成类属性,所有对象用的都是这个
        VISIT_DIC = {}
        def __init__(self):
            self.history_list=[]
        def allow_request(self, request, view):
            '''
            #(1)取出访问者ip
            #(2)判断当前ip不在访问字典里,添加进去,并且直接返回True,表示第一次访问,在字典里,继续往下走
            #(3)循环判断当前ip的列表,有值,并且当前时间减去列表的最后一个时间大于60s,把这种数据pop掉,这样列表中只有60s以内的访问时间,
            #(4)判断,当列表小于3,说明一分钟以内访问不足三次,把当前时间插入到列表第一个位置,返回True,顺利通过
            #(5)当大于等于3,说明一分钟内访问超过三次,返回False验证失败
            '''
    
            ip=request.META.get('REMOTE_ADDR')
            ctime=time.time()
            if ip not in self.VISIT_DIC:
                self.VISIT_DIC[ip]=[ctime,]
                return True
            self.history_list=self.VISIT_DIC[ip]   #当前访问者时间列表拿出来
            while True:
                if ctime-self.history_list[-1]>60:
                    self.history_list.pop() # 把最后一个移除
                else:
                    break
            if len(self.history_list)<3:
                self.history_list.insert(0,ctime)
                return True
            else:
                return False
    
        def wait(self):
            # 当前时间,减去列表中最后一个时间
            ctime=time.time()
    
            return 60-(ctime-self.history_list[-1])
    
    #全局使用,局部使用
    
    
    # SimpleRateThrottle源码分析
        def get_rate(self):
            """
            Determine the string representation of the allowed request rate.
            """
            if not getattr(self, 'scope', None):
                msg = ("You must set either `.scope` or `.rate` for '%s' throttle" %
                       self.__class__.__name__)
                raise ImproperlyConfigured(msg)
    
            try:
                return self.THROTTLE_RATES[self.scope]  # scope:'user' => '3/min'
            except KeyError:
                msg = "No default throttle rate set for '%s' scope" % self.scope
                raise ImproperlyConfigured(msg)
        def parse_rate(self, rate):
            """
            Given the request rate string, return a two tuple of:
            <allowed number of requests>, <period of time in seconds>
            """
            if rate is None:
                return (None, None)
            #3  mmmmm
            num, period = rate.split('/')  # rate:'3/min'
            num_requests = int(num)
            duration = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}[period[0]]
            return (num_requests, duration)
        def allow_request(self, request, view):
            if self.rate is None:
                return True
            #当前登录用户的ip地址
            self.key = self.get_cache_key(request, view)  # key:'throttle_user_1'
            if self.key is None:
                return True
    
            # 初次访问缓存为空,self.history为[],是存放时间的列表
            self.history = self.cache.get(self.key, [])
            # 获取一下当前时间,存放到 self.now
            self.now = self.timer()
    
            # Drop any requests from the history which have now passed the
            # throttle duration
    
            # 当前访问与第一次访问时间间隔如果大于60s,第一次记录清除,不再算作一次计数
            # 10 20 30 40
            # self.history:[10:23,10:55]
            # now:10:56
            while self.history and  self.now - self.history[-1] >= self.duration:
                self.history.pop()
    
            # history的长度与限制次数3进行比较
            # history 长度第一次访问0,第二次访问1,第三次访问2,第四次访问3失败
            if len(self.history) >= self.num_requests:
                # 直接返回False,代表频率限制了
                return self.throttle_failure()
    
            # history的长度未达到限制次数3,代表可以访问
            # 将当前时间插入到history列表的开头,将history列表作为数据存到缓存中,key是throttle_user_1,过期时间60s
            return self.throttle_success()
    
    

    2 自动生成接口文档

    # 1 安装:pip install coreapi
    
    # 2 在路由中配置
    	from rest_framework.documentation import include_docs_urls
        urlpatterns = [
            ...
            path('docs/', include_docs_urls(title='站点页面标题'))
        ]
    #3 视图类:自动接口文档能生成的是继承自APIView及其子类的视图。
    	-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:
            修改图书的阅读量
            """
    

    3 JWT

    jwt=Json Web token
    #原理
    """
    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": "安全码"
    }
    """
    
    校验
    """
    1)将token按 . 拆分为三段字符串,第一段 头加密字符串 一般不需要做任何处理
    2)第二段 体加密字符串,要反解出用户主键,通过主键从User表中就能得到登录用户,过期时间和设备信息都是安全信息,确保token没过期,且时同一设备来的
    3)再用 第一段 + 第二段 + 服务器安全码 不可逆md5加密,与第三段 签名字符串 进行碰撞校验,通过后才能代表第二段校验得到的user对象就是合法的登录用户
    """
    
    drf项目的jwt认证开发流程(重点)
    """
    1)用账号密码访问登录接口,登录接口逻辑中调用 签发token 算法,得到token,返回给客户端,客户端自己存到cookies中
    
    2)校验token的算法应该写在认证类中(在认证类中调用),全局配置给认证组件,所有视图类请求,都会进行认证校验,所以请求带了token,就会反解出user对象,在视图类中用request.user就能访问登录的用户
    
    注:登录接口需要做 认证 + 权限 两个局部禁用
    """
    
    # 第三方写好的  django-rest-framework-jwt
    # 安装pip install djangorestframework-jwt
    
    # 新建一个项目,继承AbstractUser表()
    
    # 创建超级用户
    
    # 简单使用
     #urls.py
        from rest_framework_jwt.views import ObtainJSONWebToken,VerifyJSONWebToken,RefreshJSONWebToken,obtain_jwt_token
        path('login/', obtain_jwt_token),
    
        
     
    
    

    自定制auth认证类

    from rest_framework_jwt.authentication import BaseAuthentication,BaseJSONWebTokenAuthentication
    from rest_framework.exceptions import AuthenticationFailed
    from rest_framework_jwt.authentication import jwt_decode_handler
    from rest_framework_jwt.authentication import get_authorization_header,jwt_get_username_from_payload
    from rest_framework import exceptions
    class MyToken(BaseJSONWebTokenAuthentication):
        def authenticate(self, request):
            jwt_value=str(request.META.get('HTTP_AUTHORIZATION'))
            # 认证
            try:
                payload = jwt_decode_handler(jwt_value)
    
            except Exception:
                raise exceptions.AuthenticationFailed("认证失败")
            user=self.authenticate_credentials(payload)
            return user,None
        
    #局部使用,全局使用
    

    补充

    1 函数显示传参类型和返回值类中

    作业

    1 什么是集群,什么是分布式

    作业:
    	1 自定义User表,新增mobile唯一约束字段;新增icon图片字段
    	2 在自定义User表基础上,用 GenericViewSet + CreateModelMixin + serializer 完成User表新增接口(就是注册接口)(重要提示:序列化类要重写create方法,不然密码就是明文了)
    	3 在自定义User表基础上,用 GenericViewSet + RetrieveModelMixin + serializer 完成User表单查(就是用户中心)
    	4 在自定义User表基础上,用 GenericViewSet + UpdateModelMixin + serializer 完成用户头像的修改
    
  • 相关阅读:
    (转)当别人努力的时候,你在做什么?
    《IT项目管理》读书笔记(9) —— 项目风险管理
    线程通信机制之定时器队列
    处理控制台事件消息
    C++常见内存错误及解决方案
    WCF与现行分布式通讯技术性能对比
    (译)如何使用SocketAsyncEventArgs类(How to use the SocketAsyncEventArgs class)
    常用性能计数器说明
    有关WCF公布IDataRead的问题
    负载均衡
  • 原文地址:https://www.cnblogs.com/zdw20191029/p/14553282.html
Copyright © 2011-2022 走看看