当你发现自己的才华撑不起野心时,就请安静下来学习吧。

Personal site

↑点击传送

Django---drf,自定制频率、自动生成文档、JWT

昨日回顾

#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 完成用户头像的修改
posted @ 2020-07-15 08:40  Joab-0429  阅读(329)  评论(8编辑  收藏  举报