DRF频率、自动生成接口文档、jwt简单使用


DRF

昨日回顾

#1.表关系
	OneToOneField本质其实就是foreignkey加unique
	on_delete :
    	CASCADE:这就是默认的选项,级联删除,你无需显性指定它。
        PROTECT: 保护模式,如果采用该选项,删除的时候,会抛出ProtectedError错误。
        SET_NULL: 置空模式,删除的时候,外键字段被设置为空,前提就是blank=True, null=True,定义该字段的时候,允许为空。
        SET_DEFAULT: 置默认值,删除的时候,外键字段设置为默认值,所以定义外键的时候注意加上一个默认值。
        SET(): 自定义一个值,该值当然只能是对应的实体了
    字段建索引,字段唯一
    联合索引联合唯一
    日期类型 auto_now_add 记录创建时间   
    	auth_now 修改时间
#2.book 
	单条查询 多条查询 :
		写在一个视图类,配路由时候,转到这  取出kwargs判断有没有pk 有就是单条 没有就是多条
    单条新增 多条新增:
    	直接把内容反序列化然后判断is_valid一下,调save保存 在return 一个Response一个对象
     单条修改 多条修改:
    	调用序列化器时候list_serializer_class = MyListSerializer 会走自己定义的MyListSerializer方法里面的update	
    单条删除,多条删除:
    	要注意删除修改(is_delete)
#3.频率
	自定义频率 (限制ip、id)
    -第一步:写一个类,继承SimpleRateThrottle,重写get_cache_key
    -第二步:get_cache_key返回什么就以什么做限制,
    必须写类属性 scope='字符串'限制了setting里
    -第三步:配置文件中配置
      'DEFAULT_THROTTLE_RATES': {
        '字符串': '3/m',  # key:ip_1m_3 对应频率类的scope属性, value: 3/m 一分钟访问3次
#4、分页
   PageNumberPagination,
   LimitOffsetPagination
   CursorPagination

1.频率SimpleRateThrottle源码分析

#先寻找allow_request       
	if self.rate is None:
            return True

#在进入他里面的__init__ 
def __init__(self):
        if not getattr(self, 'rate', None):
            self.rate = self.get_rate()
        self.num_requests, self.duration = self.parse_rate(self.rate)

通过反射去get_rate里面获取到了return self.THROTTLE_RATES[self.scope]  配置文件找key=scope的对应的value(3/min),回到init self.rate=3/min,在通过parse_rate把rate分别给num_requests和duration
再次回到allow_request 获取get_cache_key =key 以什么做限制,在通过获取self.history=self.cache.get(限制条件,[])  这句话意思是能取到值就是history  取不到就是空列表
然后再通过self.time() 获取当前时间  time里面是time.time的内存地址
然后 判断history有没有值并且当前时间now减去最后一次的时间-history[-1]大于或者等于>=duration  pop出去   
然后又判断了一下history长度 是否大于num_requests 这个是settings配置的3/min 的3如果大于  直接走throttle_failure 方法 直接return了false
最下面retrun  这个throttle_success
    def throttle_success(self):

        self.history.insert(0, self.now) #当前时间加入到history
        self.cache.set(self.key, self.history, self.duration) #缓存加入
        return True 

2.自动生成接口文档

#给前端写一个文档 
#安装
1.pip3 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:
    修改图书的阅读量
    """
    
#如果报错 settings加入
#AttributeError: 'AutoSchema' object has no attribute 'get_link'
REST_FRAMEWORK = {
 'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema',
    # 新版drf schema_class默认用的是rest_framework.schemas.openapi.AutoSchema

}

3.jwt

3.1jwt简单介绍

1.什么是集群: 相同代码部署在多台机器上

2.加密
"""
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": "安全码"
}
"""
3.校验
"""
1)将token按 . 拆分为三段字符串,第一段 头加密字符串 一般不需要做任何处理
2)第二段 体加密字符串,要反解出用户主键,通过主键从User表中就能得到登录用户,过期时间和设备信息都是安全信息,确保token没过期,且时同一设备来的
3)再用 第一段 + 第二段 + 服务器安全码 不可逆md5加密,与第三段 签名字符串 进行碰撞校验,通过后才能代表第二段校验得到的user对象就是合法的登录用户
"""
4.drf项目的jwt认证开发流程(重点)
"""
1)用账号密码访问登录接口,登录接口逻辑中调用 签发token 算法,得到token,返回给客户端,客户端自己存到cookies中

2)校验token的算法应该写在认证类中(在认证类中调用),全局配置给认证组件,所有视图类请求,都会进行认证校验,所以请求带了token,就会反解出user对象,在视图类中用request.user就能访问登录的用户

注:登录接口需要做 认证 + 权限 两个局部禁用
"""

第三方写好的:django-rest-framework-jwt

3.2安装jwt

pip3 install djangorestframework-jwt -i https://pypi.douban.com/simple/

3.3使用jwt

1.models 扩展user表 继承AbstractUser   数据库没迁移之前的项目
from django.contrib.auth.models import AbstractUser

class User(AbstractUser):
    phone=models.CharField(max_length=32)
    #image依赖于pillow模块
    icon=models.ImageField(upload_to='icon')

2.配置文件设置
    #一定不要忘记 app名字.表名小写
    AUTH_USER_MODEL='app01.user'   后面指定的是APP名字
    #配置图片路径
    MEDIA_URL='/media/'
    #配置图片文件夹
    MEDIA_ROOT=os.path.join(BASE_DIR,'media')

3.数据库迁移
	makemigrations
	migrate
4.创建超级用户
	creatsuperuser
5.简单快速使用(路由层配置)
	from rest_framework_jwt.views import ObtainJSONWebToken,VerifyJSONWebToken,\
    RefreshJSONWebToken,obtain_jwt_token
#基类:JSONWebTokenAPIView
#ObtainJSONWebToken,VerifyJSONWebToken,RefreshJSONWebToken都继承了JSONWebTokenAPIView
urlpatterns = [
    path('admin/', admin.site.urls),
    #下面的这两个路由一个意思
    # path('login/', ObtainJSONWebToken.as_view()),
    path('login/', obtain_jwt_token),
]
6.其他设置
settings app里面要加入
'rest_framework_jwt'
然后下面全局配置认证
REST_FRAMEWORK={
    'DEFAULT_AUTHENTICATION_CLASSES':['rest_framework_jwt.authentication.JSONWebTokenAuthentication']
}
7.写一个认证类

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
class BookView(APIView):
    def get(self,request):
        #局部配置
        authentication_class=[JSONWebTokenAuthentication,]
        return Response('ok')
    
8测试

3.4jwt源码部分

3.5自定制认证类

jwt 里面的settings里有 这一句话 利用了反射如果django有JWT_AUTH就用自己的
USER_SETTINGS = getattr(settings, 'JWT_AUTH', None)

from rest_framework_jwt.authentication import BaseAuthentication,BaseJSONWebTokenAuthentication
from rest_framework_jwt.authentication import jwt_decode_handler
from rest_framework import exceptions


class MyToken(BaseJSONWebTokenAuthentication):
    def authenticate(self, request):
        # 在headres拿到token 去认证
        token = str(request.META.get('HTTP_AUTHORIZATION'))
        # 认证
        try:
            #通过jwt_decode_handler这个方法解密出来第二部分,体里面的信息
            payload = jwt_decode_handler(token)
        except Exception:
            raise exceptions.AuthenticationFailed('认证失败')
        user=self.authenticate_credentials(payload)
        return user, None

    
#视图类里面指定为自己的认证类
authentication_classes = [MyToken, ]
drf