一、rest-framework 认证
1 介绍
由于我们每次给服务器发请求,由于Http的无状态,导致我们每次都是新的请求,
那么服务端需要对每次来的请求进行认证,看用户是否登录,以及登录用户是谁~~
那么我们服务器对每个请求进行认证的时候,不可能在每个视图函数中都写认证,rest-framwork中用户登录成功后给用户发送token,用下次请求携带token就可以认证。
2 用法
models.py
class User(models.Model):
name = models.CharField(max_length=32)
pwd = models.CharField(max_length=32)
token = models.UUIDField(null=True, blank=True)
auth.py
定义认证类
from .models import User
from rest_framework.exceptions import AuthenticationFailed
from rest_framework.authentication import BaseAuthentication
class MyAuth(BaseAuthentication):
def authenticate(self, request):
# 认证逻辑
# 拿到前端传过来的token
# 判断token是否存在
#?token=2c14fc0a-f2da-45b0-aa9b-5a406dfff462
token = request.query_params.get("token", "")
if not token:
raise AuthenticationFailed("缺少token")
user_obj = User.objects.filter(token=token).first()
if not user_obj:
raise AuthenticationFailed("token不合法")
# request.user request.auth
return (user_obj, token)
views.py
测试认证
from rest_framework.views import APIView
from rest_framework.response import Response
from .models import User
from .auth import MyAuth
import uuid #随机生成uuid码
class AuthTest(APIView):
"""登录认证后发送给前端token串"""
def post(self, request):
name = request.data.get("name", "")
pwd = request.data.get("pwd", "")
# 校验用户名和密码是否正确
user_obj = User.objects.filter(name=name, pwd=pwd).first()
if user_obj:
user_obj.token = uuid.uuid4()
user_obj.save()
return Response(user_obj.token)
else:
return Response("用户名或密码错误")
class TestView(APIView):
#认证token合法性
authentication_classes = [MyAuth, ]
def get(self, request):
return Response("测试认证组件")
- 提交post登录请求后返回token码
- 发送get请求并携带token进行验证
二、rest-framework 权限
用户登录认证后判断type字段,来判断用户的权限是否合格
class MyPermission(object):
message = "权限不足"
def has_permission(self, request, view):
# 权限逻辑
# 认证已经执行完了
user_obj = request.user #认证对obj对象
if user_obj.type == 1:
return False
else:
return True
引用
views.py
from .serializers import BookSerializer
from rest_framework import viewsets
from utils.pagination import MyPaginator, LimitOffsetPaginator, CursorPaginator
from .permission import MyPermission
from .auth import MyAuth
class BookModelView(viewsets.ModelViewSet):
queryset = models.Book.objects.all()
serializer_class = BookSerializer
authentication_classes = [MyAuth, ]
permission_classes = [MyPermission, ]
使用token进行校验
三、rest-framework 分页
分页功能分为三种类型
1. 普通分页
http://127.0.0.1:8000/book/?page=1&size=2 可以指定页码和页码数据数量
from rest_framework import pagination
class MyPaginator(pagination.PageNumberPagination):
page_size = 2
page_query_param = 'page' #手动配置路径上查询页的key的名称
page_size_query_param = 'size' #手动配饰路径上显示最多信息条数的key
max_page_size = 3 #最大不超过3
引用
class BookModelView(viewsets.ModelViewSet):
queryset = models.Book.objects.all()
serializer_class = BookSerializer
pagination_class = MyPaginator #使用分页器
2. 偏移分页
http://127.0.0.1:8000/book/?limit=1&offset=1 页面数据数量和偏移量,offset=1&limit=1表示从第2条数据开始,往后1条
from rest_framework import pagination
class LimitOffsetPaginator(pagination.LimitOffsetPagination):
default_limit = 1
limit_query_param = 'limit'
offset_query_param = 'offset'
引用
class BookModelView(viewsets.ModelViewSet):
queryset = models.Book.objects.all()
serializer_class = BookSerializer
pagination_class = LimitOffsetPaginator #使用分页器
3. 游标加密分页
http://127.0.0.1:8000/book/?cursor=cj0xJnA9Mw%3D%3D 可以进行排序,游标后为加密内容,只有上一页和下一页
from rest_framework import pagination
class CursorPaginator(pagination.CursorPagination):
cursor_query_param = 'cursor'
ordering = 'id' #按id进行排序
page_size = 1
引用
class BookModelView(viewsets.ModelViewSet):
queryset = models.Book.objects.all()
serializer_class = BookSerializer
pagination_class = CursorPaginator #使用分页器
四、rest-framework 频率
为了控制用户对某个url请求的频率,比如,一分钟以内,只能访问三次
1. 原理
- 拿到用户的IP地址
- 访问记录: {IP: [最近访问时间,…第一次的访问时间]}
- 确保这个列表最近的访问时间以及最远的访问时间差 《= 规定时间
- 判断列表的长度 《= 设置的访问频率
2.自定义频率
class MyThrottle(object):
"""
一分钟允许访问5次
"""
def __init__(self):
self.history = []
def allow_request(self, request, view):
# 获取用户的IP地址
ip = request.META.get("REMOTE_ADDR", "")
# self.key = self.get_cache_key()
# self.cache.get(self.key, [])
if ip not in VISIT_RECORD:
VISIT_RECORD[ip] = [time.time(),]
else:
history = VISIT_RECORD[ip]
self.history = history
history.insert(0, time.time())
# 确保列表时间是允许范围之内
while self.history[0] - self.history[-1] > 60:
self.history.pop()
# 判断列表长度
if not len(self.history) <= 5:
return False
return True
# 等待时间
# [最近时间, 最老时间]
def wait(self):
return 60-(self.history[0] - self.history[-1])
引用
from .serializers import BookSerializer
from rest_framework import viewsets
from utils.pagination import MyPaginator, LimitOffsetPaginator, CursorPaginator
from .throttle import MyThrottle
class BookModelView(viewsets.ModelViewSet):
queryset = models.Book.objects.all()
serializer_class = BookSerializer
pagination_class = CursorPaginator
throttle_classes = [MyThrottle, ]
3. 内置频率类使用
class DRFThrottle(SimpleRateThrottle):
scope = "WD" #定义的名字key,方便setting中去取
def get_cache_key(self, request, view):
# 拿IP地址
return self.get_ident(request)
settings.py
REST_FRAMEWORK = {
"DEFAULT_THROTTLE_RATES": {
"WD": "3/m" #一分钟三次
}
}
引用
views.py
from .serializers import BookSerializer
from rest_framework import viewsets
from utils.pagination import MyPaginator, LimitOffsetPaginator, CursorPaginator
from .throttle import MyThrottle
class BookModelView(viewsets.ModelViewSet):
queryset = models.Book.objects.all()
serializer_class = BookSerializer
pagination_class = CursorPaginator
throttle_classes = [DRFThrottle, ]
五、常见错误
“CSRF check no attribute ‘process_request’”
一定要添加在settings中的底部,底部生效,setting中的配置也需要用大写去配置。
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": []
}
注意点:
url: {“get”:“list”}
继承 : viewsets.ModelviewSet
配置:序列化,分页,认证:token,频率