权限类的使用
-写一个类,继承BasePermission
-重写has_permission方法
-在方法中校验用户是否有权限(来到权限类说明用户已经通过了认证类,request.user就是当前用户)
-如果有权限返回True,没有返回False
-self.message 返回给前端提示信息
-全局使用、局部禁用、局部使用
models.py
class UserToken(models.Model):
user = models.OneToOneField(to='User', on_delete=models.CASCADE)
token = models.CharField(max_length=32, null=True)
class User(models.Model):
username = models.CharField(max_length=32)
password = models.CharField(max_length=32)
user_type = models.IntegerField(choices=((1, '管理员'), (2, 'VIP'), (3, '屌丝用户')), default=3)
auth.py
from rest_framework.permissions import BasePermission
class UserTypePermissions(BasePermission):
def has_permission(self, request, view):
if request.user.user_type == 1:
return True
else:
self.message = '您是%s, 没有权限访问' % request.user.get_user_type_display()
return False
频率类使用
-写一个类 继承SimpleRateThrottle
-重写get_cache_key,返回唯一的字符串,会以这个字符串做频率限制
-写一个类属性:scope = '' 这个属性需要跟配置文件对应
-配置文件写:
'DEFAULT_THROTTLE_RATES': {
'xxx': '3/m'
}
-局部配置 全局配置 局部禁用
auth.py
from rest_framework.throttling import BaseThrottle, SimpleRateThrottle
class MyThrottling(SimpleRateThrottle):
scope = 'luffy'
def get_cache_key(self, request, view):
return request.META.get('REMOTE_ADDR')
settings.py
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES':['app01.throttling.MyThrottling'],
'DEFAULT_THROTTLE_RATES': {
'luffy': '3/m'
}
}
3 认证源码分析
-之前咱们读APIView的执行流程---》包装了新的request,执行了3大认证,执行视图类的方法,处理了全局异常
-入口:APIView的dispatch
-APIView的dispatch的496行上下:self.initial(request, *args, **kwargs)
-APIView的initial
-413行上下:有三句话,分别是:认证,权限,频率
self.perform_authentication(request)
self.check_permissions(request)
self.check_throttles(request)
-读认证类的源码---》APIView的perform_authentication(request),315行上下
def perform_authentication(self, request):
request.user
-request是新的request---》Request类中找user属性(方法),是个方法包装成了数据属性
-来到Request类中找:220行
def user(self):
if not hasattr(self, '_user'):
with wrap_attributeerrors():
self._authenticate()
return self._user
-Request的self._authenticate()---》373行
def _authenticate(self):
for authenticator in self.authenticators:
try:
user_auth_tuple = authenticator.authenticate(self)
except exceptions.APIException:
self._not_authenticated()
raise
if user_auth_tuple is not None:
self._authenticator = authenticator
self.user, self.auth = user_auth_tuple
return
self._not_authenticated()
4 权限源码分析
-先读最简单的权限执行流程---》APIView的check_permissions(request),325行上下
def check_permissions(self, request):
for permission in self.get_permissions():
if not permission.has_permission(request, self):
self.permission_denied(
request,
message=getattr(permission, 'message', None),
code=getattr(permission, 'code', None)
)
-APIVIew的self.get_permissions(),273行上下
return [permission() for permission in self.permission_classes]
-self.permission_classes 就是咱们在视图类中配的权限类的列表
-所以这个get_permissions返回的是 咱们在视图类中配的权限类的对象列表[UserTypePermession(),]
-为什么要写一个类,重写has_permission方法,有三个参数,为什么一定要return True或False,messgage可以做什么用
5 简单读频率类源码
-之前咱们读APIView的执行流程---》包装了新的request,执行了3大认证,执行视图类的方法,处理了全局异常
-入口:APIView的dispatch
-APIView的dispatch的496行上下:self.initial(request, *args, **kwargs)
-APIView的initial
-413行上下:有三句话,分别是:认证,权限,频率
self.perform_authentication(request)
self.check_permissions(request)
self.check_throttles(request)
-APIView的check_throttles:351上下
def check_throttles(self, request):
throttle_durations = []
for throttle in self.get_throttles():
if not throttle.allow_request(request, self):
throttle_durations.append(throttle.wait())
-总结:要写频率类,必须重写allow_request方法,返回True(没有到频率的限制)或False(到了频率的限制)
鸭子类型
不需要显示的继承某个类,只要我的类中有run和speak方法,我就是鸭子这个类
-方式一:abc模块,装饰后,必须重写方法,不重写就报错
-方式二:drf源码中使用的:父类中写这个方法,但没有具体实现,直接抛异常
作业
---------可以不需要写----
class OurThrottling(BaseThrottle):
def allow_request(self, request, view):
return False
auth.py
from app01 import models
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from rest_framework.permissions import BasePermission
from rest_framework.throttling import BaseThrottle, SimpleRateThrottle
class LoginAuth(BaseAuthentication):
def authenticate(self, request):
token = request.META.get('HTTP_TOKEN')
user_token = models.UserToken.objects.filter(token=token).first()
if user_token:
return user_token.user, token
else:
raise AuthenticationFailed('您还没登录')
class UserTypePermissions(BasePermission):
def has_permission(self, request, view):
if request.user.user_type == 1:
return True
else:
self.message = '您是%s, 没有权限访问' % request.user.get_user_type_display()
return False
class TimeThrottle(SimpleRateThrottle):
scope = 'day07_throttle'
def get_cache_key(self, request, view):
return request.META.get('REMOTE_ADDR')
views.py
import uuid
from django.shortcuts import render
from rest_framework.decorators import action
from rest_framework.response import Response
from app01 import auth
from app01 import models
from app01 import serializer
from rest_framework.viewsets import ModelViewSet, ViewSet
class BookView(ModelViewSet):
authentication_classes = [auth.LoginAuth, ]
throttle_classes = [auth.TimeThrottle, ]
serializer_class = serializer.BookSerializer
queryset = models.Book.objects.all()
class PublishView(ModelViewSet):
authentication_classes = [auth.LoginAuth, ]
permission_classes = [auth.UserTypePermissions, ]
throttle_classes = [auth.TimeThrottle, ]
serializer_class = serializer.PublishSerializer
queryset = models.Publish.objects.all()
class UserView(ViewSet):
@action(methods=['POST'], detail=False, url_path='login')
def login(self, request):
username = request.data.get('username')
password = request.data.get('password')
user = models.User.objects.filter(username=username, password=password).first()
if user:
token = uuid.uuid4()
models.UserToken.objects.update_or_create(defaults={'token': token}, user=user)
return Response({'code': 100, 'msg': '登陆成功', 'token': token})
else:
return Response({'code': 101, 'msg': '用户名或密码错误'})