drf入门规范
1、前后端开发模式:
-混合模式:也就是BBS项目
-前后端分离模式:只负责写接口,不用管前端
2、API接口:
-长得像返回数据的url链接
-请求方式:get、post、put等等
-请求参数:地址、请求体
-返回数据
-写接口为了给谁用?
前端(web、app),或者提供给第三方调用
3、接口测试工具------->postman
-发送http请求工具
-get请求可以在请求体中携带数据
-get请求和post请求有什么区别?
-请求编码格式:
-urlencoded:key=value&key=value------->从request.POST中取
-form-data:数据和文件混在一起------->文件从request.FILES中取,数据从request.POST中取
-json:{"name":"XXX"}------->>数据从request.body中取
-collections创建,保存导出
4、接口规范,restful规范:
-数据安全的保障
-接口特征的变现
-多数据版本共存
-数据既是资源,均使用名词(可复数)
-资源操作由请求的方式来决定(method)
-过滤,通过在url上传参的形式传递搜索条件
-响应状态码(两层)
-错误处理,应返回错误信息,error当key
-返回结果,针对不同操作,服务器向用户返回的结果应该符合以下规范
-需要url请求的资源需要访问资源的请求链接
5、序列化和反序列化:
-序列化:将我的数据类型转换成json格式字符串可供其他语言使用
-反序列化:将其他语言提供给我们的数据类型,转换成我们自己可以识别的数据类型
6、drf:
Django框架上的一款APP,方便我们快速编写符合restful规范的接口
7、CBV源码分析:
.....
8、APIView比View多出了哪些东西
-在dispatch中执行了视图类中与请求方式同名的方法
-去掉了csrf验证
-包装了全新的request
-执行了三大认证
-处理了全局异常
-可以直接在视图类中的方法中self.request
get请求和post请求的区别
GET请求和POST请求是HTTP协议中常用的两种请求方法,它们在以下几个方面有所区别:
1. 参数传递方式:
- GET请求:参数通过URL的查询字符串(query string)传递,即参数会附加在URL的末尾,使用`?`进行分隔,多个参数之间使用`&`符号连接。例如:`http://example.com/path?param1=value1¶m2=value2`
- POST请求:参数通过请求体(request body)传递,参数不会附加在URL中,而是在请求体中进行传递。参数的格式可以是表单形式(`application/x-www-form-urlencoded`)或者是JSON等其他格式。
2. 参数长度限制:
- GET请求:由于参数附加在URL中,URL的长度是有限制的,不同的浏览器和服务器对URL长度的限制不一样,一般在2KB到8KB之间。超过限制的参数可能会被截断或丢失。
- POST请求:请求体的长度一般没有明确的限制,但是具体的限制取决于服务器的配置和资源。
3. 安全性:
- GET请求:参数附加在URL中,会被保存在浏览器的历史记录、服务器的日志文件等地方,容易被他人获取。因此,不适合传递敏感信息(如密码)。
- POST请求:参数在请求体中传递,不会被保存在URL中,相对于GET请求更安全,适合传递敏感信息。
4. 幂等性:
- GET请求:一般情况下是幂等的,即多次重复请求不会产生副作用。
- POST请求:一般情况下是非幂等的,即多次重复请求可能会产生副作用,如创建重复的资源。
5. 缓存:
- GET请求:可以被浏览器缓存,可以通过在请求头中设置`Cache-Control`等字段来控制缓存策略。
- POST请求:一般不会被浏览器缓存。
根据具体的需求和场景,选择合适的请求方法是很重要的。一般来说,GET请求适合获取资源,而POST请求适合提交数据或执行操作。
http和https协议的区别
HTTP(Hypertext Transfer Protocol)和HTTPS(Hypertext Transfer Protocol Secure)是两种常见的网络传输协议,它们在以下几个方面有所区别:
1. 安全性:
- HTTP:是明文传输协议,数据传输过程中不进行加密,容易被窃听和篡改。因此,HTTP不适合传输敏感信息。
- HTTPS:是通过使用SSL(Secure Sockets Layer)或TLS(Transport Layer Security)协议对HTTP进行加密的协议。通过使用公钥加密和私钥解密的方式,HTTPS可以保证数据在传输过程中的安全性,防止数据被窃听和篡改。
2. 默认端口:
- HTTP:默认使用端口80进行通信。
- HTTPS:默认使用端口443进行通信。
3. 证书:
- HTTP:不需要证书。
- HTTPS:需要使用SSL证书,证书由可信的第三方机构(CA)签发,用于验证服务器的身份。证书包含了服务器的公钥,用于加密数据传输。
4. 性能:
- HTTP:由于数据传输过程中没有加密和解密的过程,相对于HTTPS来说,性能更高。
- HTTPS:由于数据传输过程中需要进行加密和解密的过程,相对于HTTP来说,性能略低。
5. SEO(Search Engine Optimization):
- HTTP:搜索引擎更容易识别和索引HTTP网页。
- HTTPS:搜索引擎更倾向于将HTTPS网页排名更高,因为HTTPS可以提供更安全的用户体验。
需要注意的是,为了提高网站的安全性和保护用户的隐私,现在越来越多的网站采用了HTTPS协议。在使用HTTPS时,网站需要获得有效的SSL证书,并进行相应的配置。
序列化组件
1、序列化组件的介绍:
-可以对数据进行序列化------->queryset对象
-可以对数据进行反序列化------->前端出入的json格式的数据------->保存到数据库中
-可以对数据进行反序列化检验------->字段自己的校验,局部钩子、全局钩子
2、序列化组件的快速使用:
-先写一个类继承继承Serializer
-在类中写入字段
-在视图类中:实例化得到序列化类的对象
-序列化:序列化类的对象.data retrun Response(ser.data)
3、常用字段类
-跟models里面的没有区别,只是多出来两个:DictField、ListField,这两个参数一个接收字典类型、一个接受列表类型,这两个字段都可以进行序列化和反序列化
4、字段参数:
-read_only = True 只做序列化
-write_only = False 只做反序列化
5、继承Serializer做反序列化新增或修改组需要重写create方法和updata方法
-新增
ser=BookSerializer(data=request.data)
ser.is_valid(raise_exception=True)--->只要校验不通过,直接抛异常
ser.save() # 继承Serializer,需要在序列化类中重写create方法,完成新增
序列化类中重写create方法
def create(self, validated_data):
# validated_data 前端传入,校验过后的数据
# 新增一个对象
return 新增的对象 # 后续再视图类中只要调用ser.data,这里必须返回
-修改
ser=BookSerializer(instance=要修改的对象,data=request.data)
ser.is_valid(raise_exception=True)--->只要校验不通过,直接抛异常
ser.save() # 继承Serializer,需要在序列化类中重写update方法,完成修改
序列化类中重写update
def update(self, instance, validated_data):
# instance 要修改的对象,哪里来的?BookSerializer(instance=要修改的对象的id,data=request.data)
# validated_data:校验过后数据
res=Book.objects.filter(pk=instance).update(**validated_data)
return res # 返回的res最后干啥了?只要在 视图类中 调用ser.data,他会根据这个返回值做序列化
6、反序列化校验:
字段自己校验----->局部钩子----->全局钩子
7、返回定制格式:
-1、source:
-1、改写字段名
-2、跨表查询
-3、拿到表模型中方法
-2、SerializerMethodField:在序列化类中写
-publish = Serializers.SerializerMethodField(read_only = True)
配合一个get_publish(self,obj)方法就可以定制该字段的返回格式
-3、在表模型中写方法:
def publishdict(self):
return {"name":publish.name}
在序列化类中:publishdict = Serializers.DictField()
-只能做序列化用了,反序列化得单独用----->需要使用read_only和write_only控制
8、ModelSerializer使用:
class Auth(Serializers.ModelSerializer):
或者可以在此处重写字段
class Meta:
model = 表名
fields = "__all__" or ["字段名","字段名"......]
extra_kwargs={} # 传入字段参数
大部分情况下不需要写create和update了
局部,全局钩子跟之前一模一样
视图组件
1、两个视图基类:
APIView:
-类属性:
renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
parser_classes = api_settings.DEFAULT_PARSER_CLASSES
authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES
permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES
GenericAPIView:
-类属性:
-queryset = None
-serializer_class = None
-lookup_field = 'pk'
-filter_backends = api_settings.DEFAULT_FILTER_BACKENDS
-pagination_class = api_settings.DEFAULT_PAGINATION_CLASS
-类方法:
-get_queryset:获取所有要序列化的数据
-get_obj:获取单个对象
-get_serializer:------>调用了get_serializer_class------>返回了序列化类的对象以及要传入的参数----->instance,data,many
-filter_queryset----->给ListModelMixin用的,把配置的filter_backends 依次执行完成过滤
2、5个视图扩展类需要搭配GenericAPIView使用
-ListModelMixin,
-CreateModelMixin,
-UpdateModelMixin,
-DestroyModelMixin,
-RetrieveModelMixi
-如果一个视图类要写四个接口,必须要借助于ViewSetMixin餐可以完成
3、九个视图子类:
九个视图子类是继承了GenericAPIView以及5个视图扩展类搭配出来的
...........略
4、视图集:
ViewSetMixin:改变了路由的写法
诞生出来的子类:
-ReadOnlyModelViewSet:RetrieveModelMixin + ListModelMixin + GenericViewSet
-ModelViewSet:GenericViewSet+5个视图扩展类
-ViewSet:ViewSetMixin + APIView
-GenericViewSet:ViewSetMixin + GenericAPIView
请求与响应
1、请求:
-请求源码:request对象
-request.data:前端传入的数据
-request.query_params:url中携带的参数
-request._request:未封装前的request
-为什么我封装了request以后还可以使用以前的方法?
-因为在新的request中有一个__getattr__方法,会将为封装前的方法反射到我封装后的request方法上面
-请求能解析编码格式:parser_classes
-局部使用
-全局使用
2、响应
-响应Response 源码
-data:响应体的内容,序列化后的数据给了他
-headers:响应头 django原生响应头 响应对象['xx']=xxx
-status:响应状态码
-响应编码格式-->浏览器,postman,看到的样子
-局部和全局配置
# 写了个视图类 获取所有接口
class BookView(ViewSetMixin,GenericAPIView,ListModelMixin):
queryset = None
serializer_class = None
def list(self,request,*args,**kwargs):
res=super().list(request,*args,**kwargs)
# 返回字典格式
res.data # {code:100,msg:成功,data:[{},{}]}
return Response({code:100,msg:成功,data:res.data})
路由组件
1 只要继承ViewSetMixin及其子类,路由写法就变了
-方式一:映射方式
视图类.as_view({'get':'lqz'})
-方式二:自动生成
-SimpleRouter DefaultRouter:区别是DefaultRouter会带根目录
2 action装饰器:methods:请求方式 detail:为True URL中带数字 ,为False 则不带
认证权限频率
1、认证类的使用:
-1、写一个类,继承BaseAuthentication
-2、重写 def authenticate(self, request):方法
-3、取出用户传入的token----->通过token判断登录的用户
-4、认证成功取出用户,认证失败抛出异常
-5、return 查到的对象、token,后续通过request.user取出返回的第一个值,request.auth取出第二个值
2、权限类:
-1、写一个类,继承BasePermission
-2、重写 def has_permission(self, request, view):方法
-3、判断用户的权限
acl:权限,取出当前用户所有权限,判断当次请求是否在权限中
rbac:根据当前用户取出所有角色,通过角色取出所有权限,去个重,判断是否在权限中
-4、有权限返回True,没有权限返回False
-5、在视图类中配置或者全局配置
3、频率类:
-1、写一个类,继承SimpleRateThrottle
-2、重写 def get_cache_key(self, request, view):方法
-3、类属性配置 scope = "字符串"
-4、配置文件中配置
'DEFAULT_THROTTLE_RATES':
{
'字符串': '3/m',
}
-5、视图类中配置,全局配置
为什么我们写了 权限类,配置行,它就会执行权限控制?
-APIView 的dispatch中执行了3大认证---》
def dispatch(self, request, *args, **kwargs):
self.initial(request, *args, **kwargs) # 三大认证
initial源码:
def initial(self, request, *args, **kwargs):
self.perform_authentication(request) # 认证类
self.check_permissions(request) # 权限类
self.check_throttles(request) # 频率类
权限类源码分析
入口:self.check_permissions(request)
def check_permissions(self, request):
# for循环我这个对象列表
for permission in self.get_permissions():
# 如果has_permission(request, self)返回的是False,注意括号内的这个self是视图类的对象
if not permission.has_permission(request, self):
# 如果有任何一个权限对象返回False,则调用permission_denied方法抛出异常。同时,可以通过getattr方法获取权限对象的message和code属性,并作为参数传递给permission_denied方法
self.permission_denied(
request,
message=getattr(permission, 'message', None),
code=getattr(permission, 'code', None)
)
def get_permissions(self):
# 使用列表生成式从permission_classes取出一个个自己写的权限类,实例化得到的对象列表返回出去
return [permission() for permission in self.permission_classes]
认证类源码分析
入口:self.perform_authentication(request)
def perform_authentication(self, request):
# 点进来发现只有一个request.user,所以我们要去Request里面去找User的方法看看是不是方法为装成了属性
request.user
Request类中:
@property
def user(self):
# 如果request这个对象中没有_user这个属性就会走下一步
if not hasattr(self, '_user'):
# 使用with语句和wrap_attributeerrors()上下文管理器,捕获AttributeError异常。
with wrap_attributeerrors():
self._authenticate()
# 如果有这个属性直接返回出去
return self._user
_authenticate方法:
def _authenticate(self):
# for循环我视图类中的认证类的对象列表
for authenticator in self.authenticators:
try:
# 调用我自己写的认证类中的authenticate方法,这个地方的self是request的对象
user_auth_tuple = authenticator.authenticate(self)
except exceptions.APIException:
self._not_authenticated()
raise
# 如果user_auth_tuple不为None
if user_auth_tuple is not None:
# _authenticator拿到的就是我的认证类
self._authenticator = authenticator
# 通过解压赋值将用户给user,token给auth
self.user, self.auth = user_auth_tuple
return
self._not_authenticated()
-认证类可以配置多个,但是如果有一个返回了,后续的就不走了
self.authenticators 是request对象的属性,是在Request实例化的时候传入的,它什么时候实例化的,包装新的Reqeust时传入的--->APIView的dispatch-->request = self.initialize_request(request, *args, **kwargs),initialize_request下的------>authenticators=self.get_authenticators()下的get_authenticators------>
def get_authenticators(self):
# 通过里边生成式遍历我视图类中的列表,然后实例化得到一个对象
return [auth() for auth in self.authentication_classes]
频率源码分析
源码入口:self.check_throttles(request)
def check_throttles(self, request): 方法
def check_throttles(self, request):
throttle_durations = []
# 遍历我频率类对象的列表
for throttle in self.get_throttles():
# 执行频率类中的allow_request,并传入两个参数一个是一个request一个是我试图类的对象,返回True或者False,如果返回True
if not throttle.allow_request(request, self):
# 显示还剩多长时间才可以进行访问
throttle_durations.append(throttle.wait())
# 调用了这个方法
def get_throttles(self):
# 使用列表生成式循环我视图类中的频率类列表,并调用放到一个列表中
return [throttle() for throttle in self.throttle_classes]
频率类要写
1 写一个类,继承,BaseThrottle
2 在类中重写:allow_request方法,传入 3个参数
3 在allow_request写限制逻辑,如果还能访问--》返回True
4 如果超了次数,就不能访问,返回False
5 局部配置在视图类上
6 全局配置在配置文件中
我们自己写频率类的时候,继承了SimpleRateThrottle只重写了 def get_cache_key(self, request, view):方法,猜测可能是SimpleRateThrottle帮我们重写了该方法
SimpleRateThrottle 源码分析:
def allow_request(self, request, view):
# 咱们没写,以后咱们可以在频率类中直接写
# rate='3/m' 以后不用写scope了,就会按一分钟访问3次现在
if self.rate is None:
return True
# 调用我的get_cache_key方法返回的值,咱们返回了访问者ip
self.key = self.get_cache_key(request, view)
# 判断是否为None,为None返回True
if self.key is None:
return True
# 根据当前访问者ip,取出 这个人的访问时间列表 [访问时间1,访问2,访问3,访问4]
self.history = self.cache.get(self.key, [])
# 得到当前时间
self.now = self.timer()
# 去除掉访问时间大于60秒的
while self.history and self.history[-1] <= self.now - self.duration:
self.history.pop()
# 判断这个列表长度是否大于3,如果大于等于3返回False
if len(self.history) >= self.num_requests:
return self.throttle_failure()
# 否则调用throttle_success方法
return self.throttle_success()
# 在SimpleRateThrottle实例化的时候会触发这个方法
def __init__(self):
# 判断有没有rate这个属性
if not getattr(self, 'rate', None):
# 如果没有调用get_rate()方法来获取rate的值并赋给self.rate
# self.rate拿到的是'3/m'
self.rate = self.get_rate()
# 如果有值将会调用parse_rate方法,将self.tate传入,将返回的结果分别给self.num_requests, self.duration
self.num_requests, self.duration = self.parse_rate(self.rate)
# def get_rate(self):方法
def get_rate(self):
# 判断有没有scope这个属性
if not getattr(self, 'scope', None):
# 如果没有直接抛异常
msg = ("You must set either `.scope` or `.rate` for '%s' throttle" %
self.__class__.__name__)
raise ImproperlyConfigured(msg)
try:
"""
频率类中:
scope = xxx
'DEFAULT_THROTTLE_RATES': {
'xxx': 3/m,
'anon': None,
}
"""
# 从配置文件中拿到scope这个属性对应的值,返回出去
return self.THROTTLE_RATES[self.scope]
except KeyError:
msg = "No default throttle rate set for '%s' scope" % self.scope
raise ImproperlyConfigured(msg)
# def parse_rate(self, rate)
def parse_rate(self, rate):# rate是3/m
# 判断rate是否为空,如果为空返回None
if rate is None:
return (None, None)
# 按照/进行切分,把3给num,m给period
num, period = rate.split('/')
# 将num转成一个整型
num_requests = int(num)
# 判断我period索引为0位置的字符在不在我这个字典中
duration = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}[period[0]]
# 返回我的数字3以及m对应的60
return (num_requests, duration)
# def throttle_success(self):方法
def throttle_success(self):
# 在self.history列表第0个位置插入一条当前时间
self.history.insert(0, self.now)
# 使用self.cache.set方法将更新后的历史记录列表存储到缓存中,缓存键为self.key,过期时间为self.duration,表示该缓存记录在一定时间后过期。
self.cache.set(self.key, self.history, self.duration)
return True
继承BaseThrottle自定义的频率类
class MyThrottle(BaseThrottle):
VISIT_RECORD = {}
def allow_request(self, request, view):
# 写限制逻辑
# (1)取出访问者ip
user_ip = request.META.get('REMOTE_ADDR')
# (2)判断当前ip不在访问字典里,添加进去,并且直接返回True,表示第一次访问,在字典里,继续往下走
if user_ip not in self.VISIT_RECORD:
self.VISIT_RECORD[user_ip] = [datetime.datetime.now()]
print("++++++", self.VISIT_RECORD)
return True
# (3)循环判断当前ip的列表,有值,并且当前时间减去列表的最后一个时间大于60s,把这种数据pop掉,这样列表中只有60s以内的访问时间,
visit_times = self.VISIT_RECORD[user_ip]
# visit_times = [visit_time for visit_time in visit_times if (datetime.datetime.now() - visit_time).seconds
# <= 60]
filtered_visit_times = []
for visit_time in visit_times:
if (datetime.datetime.now() - visit_time).seconds <= 60:
filtered_visit_times.append(visit_time)
# (4)判断,当列表小于3,说明一分钟以内访问不足三次,把当前时间插入到列表第一个位置,返回True,顺利通过 (5)当大于等于3,说明一分钟内访问超过三次,返回False验证失败
if len(filtered_visit_times) < 3:
# 将当前时间插入到列表的第一个位置
filtered_visit_times.insert(0, datetime.datetime.now())
print("------", filtered_visit_times)
self.VISIT_RECORD[user_ip] = filtered_visit_times
return True
return False
# 频率类写好,配置在视图类上,就会走它
继承SimpleRateThrottle自定义频率类
class IPRateThrottle(SimpleRateThrottle):
scope = "Wayyy"
def get_cache_key(self, request, view):
token = request.GET.get("token")
user_id = request.user.pk
return f"{self.scope}:{token}:{user_id}"
过滤排序分页全局异常
过滤
针对于查询所有接口----->继承:GenericAPIView+ListModelMixin--->只需要在视图类中写一个类属性----->filter_backends = [过滤类,过滤类2]
1 内置的:SearchFilter 类属性:search_fields=[可以按字段过滤]
127.0.0.1:8080/books/?search=红
2 第三方
-django-filter
-精准匹配
127.0.0.1:8080/books/?name=红楼梦
-能更强大
3 自定义的
写一个类,继承:BaseFilterBackend
重写 filter_queryset
在filter_queryset中完成过滤,会把qs传入,返回过滤后的qs即可
源码分析:
get请求来了------>会触发我list方法的执行,会调用GenericAPIView中的filter_queryset方法,在这个方法中会将filter_backends强转成列表形式(所以我在视图类中也可以直接让filter_backends=过滤类),然后使用for循环遍历这个列表,然后再实例化我这个过滤类,执行里边的filter_queryset方法进行筛选数据,将筛选后的数据返回出去
继承APIView写过滤:
request 取出过滤条件
book_list=Book.objects.all().filter(过滤)
排序
针对于查询所有接口---》继承:GenericAPIView+ListModelMixin---》只需要在视图类中写一个类属性---》filter_backends = [排序类]
内置排序类即可:OrderingFilter---》配置类属性ordering_fields
自定义排序类,完成自己的规则
分页
针对于查询所有接口-->才有分页
三种分页方式,必须继承分页类,重写几个类属性实现--->配置在视图类上-->继承:GenericAPIView+ListModelMixin
PageNumberPagination:用的最多,之前常见的分页方式,查询第几页,每页有多少条的分页方式 :page=10&size=3
LimitOffsetPagination:从第几条开始,取几条 offset=10&limit=3 从第10条开始,取3条
CursorPagination:只能上一页和下一页,需要排好序再分页
继承APIView 写分页:
class Pager(APIView):
def get(self,request,*args,**kwargs):
# 获取所有数据
ret=models.Book.objects.all()
# 创建分页对象
page=PageNumberPagination()
# 在数据库中获取分页的数据
page_list=page.paginate_queryset(ret,request,view=self)
# 对分页进行序列化
ser=BookSerializer1(instance=page_list,many=True)
# return Response(ser.data)
# 这个也是返回Response对象,但是比基本的多了上一页,下一页,和总数据条数(了解即可)
return page.get_paginated_response(ser.data)
return Response(ser.data) # 只会有数据,不会有上一页和下一页,总条数
分页源码分析:
get请求过来----->执行list方法,先进行过滤,过滤完以后,开始分页,会执行GenericAPIView中的paginate_queryset方法,这个方法又会触发paginator的方法执行,在paginator的方法中会调用pagination_class属性,返回我分页类的一个对象,在paginate_queryset会拿到这个对象,判断是否为None,如果为None返回None,否则就
会调用分页类中的paginate_queryset方法,来完成分页
全局异常处理
前后端分离了,后期,后端出了异常,我们不想让前端看到,我们需要捕获全局异常,统一返回格式
之所以能捕捉到异常是因为在APIView中的dispatch中try了一下,如果出现异常了就会执行APIView中的handle_exception方法,这个方法中需要传入一个我的异常信息,这个方法会调用get_exception_handler方法,get_exception_handler方法就是在配置文件中拿到我配置的捕捉异常的那个函数,如果我没有配置就会用drf中的那个捕捉异常的函数
自己处理全局异常的步骤:
1 写一个函数,在内部处理
from rest_framework.views import exception_handler
def common_exception_handler(exc,context):
res=exception_handler(exc,context)
if res: #这次异常是drf的,并且它处理了
# 我们要统一返回格式
return Response({'code':888,'msg':"系统异常(drf异常),请联系系统管理员:%s"%res.data.get('detail',None)})
else:
return Response({'code': 999, 'msg': "系统异常(非drf异常),请联系系统管理员:%s" % str(exc)})
2 配置在配置文件上
接口文档
# 接口写完,必须编写接口文档,给别人用
# 接口文档规范:要有什么东西
1 描述
2 地址
3 请求方式
4 请求编码格式
5 请求参数,详解
6 返回示例 json
7 返回示例中字段详解
# 编写的位置:
1 直接写在文件中共享(word,md)
2 平台上写
- 公司搭建的平台(自己研发,第三方开源)
- 使用第三方接口文档平台
3 自动生成
-coreaip
JWT认证
# JWT是什么
json web tokne缩写,一种前后端认证的方法,区别与session的方案,不需要在后端存储数据,也能实现会话保持
# jwt原理
-三段式:
-签发阶段
-登录:
用户名+密码
手机号+验证码
本机号码一键登录--》向后端就只传了手机号--》根据手机号签发token
-认证阶段
-drf中的认证类
-校验token是否合法
# 别人截获到token后,模拟发送请求
-1 设置过期时间---》10m
-2 需要登录后才能访问的接口,不仅要带token还是带个加密串 sign
-3 双token认证
-返回两个token:一个token:7天过期,一个token:10分钟过期
-以后用就只带10分钟过期的token
-过了10分钟了,10分钟token失效了
-携带着7的token到后端,生成一个10分钟的返回
-原来的token串使用md5+盐生成一个串--》签名
# django中使用jwt--》快速签发
# 快速签发定制返回格式
# 认证:jwt提供的认证 一个认证类,一个权限类
# 自定义用户表
签发
payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)
认证:认证类
token = request.META.get('HTTP_TOKEN')
payload = jwt_decode_handler(token)
user = User.objects.get(pk=payload.get('user_id'))
# 多方式登录(auth的user表)
-把校验和签发token逻辑写在序列化类的 全局钩子中了
-在全局钩子中,只要校验不通过,就抛异常
权限控制
# acl:访问控制列表
用户跟权限多对多
# 基于角色的访问控制 rbac
用户跟角色关系,角色和权限管理
-用户表
-角色表
-权限表
-用户和角色多对多中间表
-角色和权限多对多中间表
-用户和权限多对多
# abac:
基于属性+角色的访问控制
基于属性+访问控制列表
张三:【看视频,发视频】
张三:晚上12点凌晨7点 不能发视频
作业
# 1 整理回顾的drf内容
# 2 读 权限和认证源码
# 3 读 频率源码
# 4 自己写个频率类,实现 一分钟访问3次,根据ip地址限制
from rest_framework.throttling import BaseThrottle
class MyThrottle(BaseThrottle):
VISIT_RECORD = {'192.168.1.1':[当前时间,当前时间,访问时间列表]}
def allow_request(self, request, view):
# 写限制逻辑
# (1)取出访问者ip
# (2)判断当前ip不在访问字典里,添加进去,并且直接返回True,表示第一次访问,在字典里,继续往下走
# (3)循环判断当前ip的列表,有值,并且当前时间减去列表的最后一个时间大于60s,把这种数据pop掉,这样列表中只有60s以内的访问时间,
# (4)判断,当列表小于3,说明一分钟以内访问不足三次,把当前时间插入到列表第一个位置,返回True,顺利通过
# (5)当大于等于3,说明一分钟内访问超过三次,返回False验证失败
return False
# 频率类写好,配置在视图类上,就会走它
写个频率类,实现 一分钟访问3次,根据ip地址限制
class MyThrottle(BaseThrottle):
VISIT_RECORD = {}
def allow_request(self, request, view):
# 获取到当前访问对象的IP
user_ip = request.META.get('REMOTE_ADDR')
# 如果当前IP不在这个字典中
if user_ip not in self.VISIT_RECORD:
# 以user_ip为key值,当前时间为value值保存到字典中
self.VISIT_RECORD[user_ip] = [datetime.datetime.now()]
# 返回True
return True
# 取出当前IP的列表赋给visit_times
visit_times = self.VISIT_RECORD[user_ip]
# 定义一个空的列表
filtered_visit_times = []
# 循环我IP的那个列表赋给visit_time
for visit_time in visit_times:
# 判断当前时间减去我遍历出来的时间是否小于60秒
if (datetime.datetime.now() - visit_time).seconds <= 60:
# 如果小于60秒将这个时间追加到filtered_visit_times列表中
filtered_visit_times.append(visit_time)
# 如果我filtered_visit_times列表小于3
if len(filtered_visit_times) < 3:
# 我在filtered_visit_times第0个位置插入一条当前的时间
filtered_visit_times.insert(0, datetime.datetime.now())
# 我再将filtered_visit_times这个列表赋给VISIT_RECORD[user_ip]这个列表
self.VISIT_RECORD[user_ip] = filtered_visit_times
return True
return False