前言
DRF作为django的伴生框架,也封装了很多及其好用的东西
1.认证 2.权限 3.限流 4.序列化 5.分页
认证
Drf内置的四种API认证方式:
认证方式说明:
**BasicAuthentication**每次提交请求的时候附加用户名和密码来进行认证
**TokenAuthentication**每次提交请求的时候在HTTP headers里附加Token进行认证
**SessionAuthentication**用户登录之后系统在cookies存入sessionid进行认证
**RemoteUserAuthentication**通过web服务器认证(apache/nginx这些)
我选择的是基于Token的认证,客户端登录之后维护一个token,每次请求附加到HTTP headers,还算是方便。
Drf还可以自定义认证方式,只要继承
authentication.BaseAuthentication
这个类然后重写authenticate
方法就好了。
class MyAuthentication(BaseAuthentication):
def authenticate(self, request):
# 认证逻辑,如果认证通过,返回两个值
# 如果认证失败,抛出Authentication异常
token = request.GET.get('token')
if token:
user_token = UserToken.objects.filter(token=token).first()
# 认证通过
if user_token:
return user_token.user,token
else:
raise AuthenticationFailed('认证失败')
else:
raise AuthenticationFailed('请求地址中需要携带token')
然后在视图中使用即可
class StudentViewSet(viewsets.ModelViewSet):
authentication_classes = [SessionAuthentication, BasicAuthentication,MyAuthentication]
permission_classes = [IsAuthenticated]
queryset = Student.objects.all()
serializer_class = StudentSerializer
- 创建认证类:继承
BaseAuthentication
、重写authenticate
方法 authenticate()
返回值
None
:当前认证不管,等下一个认证来执行raise exceptions.AuthenticationFailed('用户认证失败')
- 有返回值元组形式:(元素1,元素2)元素1复制给
request.user
、元素2复制给request.auth
在settings.py
中可以配置默认的认证方式,这里我添加了三个:
REST_FRAMEWORK = {
# 身份验证
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.TokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
)
}
权限
API授权
Drf的接口权限有以下几种:
- **AllowAny**:允许所有,登不登录无所谓
- **IsAuthenticated**:登录了才能访问
- **IsAdminUser**:管理员才能访问
- **IsAuthenticatedOrReadOnly**:顾名思义,不登录只读,登录才能写入
- **DjangoModelPermissions**:根据Django Auth的配置(权限细化到每个model)
- DjangoModelPermissionsOrAnonReadOnly
- **DjangoObjectPermissions**:配合第三方权限控制,细化到每个对象
一般来说小网站用到DjangoModelPermissions
就是够用的,或者干脆简单一点,用IsAuthenticated
和queryset
限定请求的数据即可。
介绍完了基本概念,来看看代码中是如何操作的。
对于操作用户信息的viewset,我只用了permissions.IsAuthenticated
这个权限,然后覆盖了ReadOnlyModelViewSet
的get_queryset
方法,把queryset变成只包括当前用户,这样就保证了一个用户只能操作自己的信息。
from rest_framework import authentication, permissions, viewsets
class UserViewSet(viewsets.ReadOnlyModelViewSet):
permission_classes = [permissions.IsAuthenticated]
serializer_class = UserSerializer
def get_queryset(self):
return User.objects.filter(pk=self.request.user.pk)
viewset的action同样可以使用权限,加在装饰器的参数上即可:
@action(detail=True, methods=['GET'], permission_classes=[permissions.IsAuthenticated])
def some_actions(self, request, pk=None):
dosomething
return Response(SomeSerializer(some_data, many=True).data)
这里提一下装饰器的detail参数,这个代表了是对列表操作还是对单个对象操作,True就是对单个对象。
ApiView和ViewSet同样通过在类字段中加入
authentication_classes
和permission_classes
实现认证和授权。
分页 PAGINATION
Drf和Django一样自带分页功能,很好用(当然也支持使用第三方的分页功能)。
首先进行配置(不配置的话使用默认配置),这里我设置每页显示十条记录:
REST_FRAMEWORK = {
# 设置分页
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 10,
}
使用得最多的ModelViewSet
已经自带分页了,这个我们不用操心,不过如果自己定义了action来返回列表数据的话,就没有分页,这时候要用paginate_queryset
方法来处理。
代码如下:
@action(detail=False)
def tag(self, request):
queryset = SomeModel.objects.all().order_by('-add_time')
page = self.paginate_queryset(queryset)
if page is not None:
return self.get_paginated_response(self.get_serializer(page, many=True).data)
return Response(self.get_serializer(queryset, many=True).data)
可以看出Drf自动处理了不同页面的请求,不用像Django一样自己从GET或者POST数据里读取page,分页相关的方法直接在viewset对象里面,非常方便。
限流
其实就是一个自定义的认证过程。
Drf内置有BaseThrottle
、SimpleRateThrottle
,后者是前者的之类。
BaseThrottle
需要自己写allow_request
和wait
方法,控制粒度更细SimpleRateThrottle
重写get_cache_key
和设置scope
名称就可以,更简单
实现1分钟内只能访问3次的限流
SimpleRateThrottle
代码如下:
from rest_framework.throttling import SimpleRateThrottle
class VisitThrottle(SimpleRateThrottle):
'''匿名用户60s只能访问三次(根据ip)'''
scope = 'throttle' #这里面的值,自己随便定义,settings里面根据这个值配置throttle
def get_cache_key(self, request, view):
#通过ip限制节流
return self.get_ident(request)
class UserThrottle(SimpleRateThrottle):
'''登录用户60s可以访问10次'''
scope = 'userThrottle' #这里面的值,自己随便定义,settings里面根据这个值配置userThrottle
def get_cache_key(self, request, view):
return request.user.user_id
BaseThrottle
代码如下:
from rest_framework.throttling import BaseThrottle
import time
VISIT_RECORD = {} #保存访问记录
class VisitThrottle(BaseThrottle):
'''60s内只能访问3次'''
def __init__(self):
self.history = None #初始化访问记录
def allow_request(self,request,view):
#获取用户ip (get_ident)
remote_addr = self.get_ident(request)
ctime = time.time()
#如果当前IP不在访问记录里面,就添加到记录
if remote_addr not in VISIT_RECORD:
VISIT_RECORD[remote_addr] = [ctime,] #键值对的形式保存
return True #True表示可以访问
#获取当前ip的历史访问记录
history = VISIT_RECORD.get(remote_addr)
#初始化访问记录
self.history = history
#如果有历史访问记录,并且最早一次的访问记录离当前时间超过60s,就删除最早的那个访问记录,
#只要为True,就一直循环删除最早的一次访问记录
while history and history[-1] < ctime - 60:
history.pop()
#如果访问记录不超过三次,就把当前的访问记录插到第一个位置(pop删除最后一个)
if len(history) < 3:
history.insert(0,ctime)
return True
def wait(self):
'''还需要等多久才能访问'''
ctime = time.time()
return 60 - (ctime - self.history[-1])
配置节流
#全局
REST_FRAMEWORK = {
# 设置全局节流
"DEFAULT_THROTTLE_CLASSES":['api.utils.throttle.UserThrottle'], #全局配置,登录用户节流限制(10/m)
# 设置访问频率
"DEFAULT_THROTTLE_RATES":{
'throttle':'3/m', #没登录用户3/m,throttle就是scope定义的值,通过IP地址
'userThrottle':'10/m', #登录用户10/m,userThrottle就是scope定义的值, 通过user_id
}
}
# 局部:在类视图中添加
throttle_classes = [VisitThrottle,]
- Post link: https://www.godhearing.cn/drf-ji-ben-cao-zuo/
- Copyright Notice: All articles in this blog are licensed under unless otherwise stated.