drf-day07
权限类使用
权限控制可以限制用户对于视图的访问和对于具体数据对象的访问。
使用步骤:
- 写一个类,继承BasePermission
- 重写has_permission方法
- 在方法中校验用户是否有权限(request.user就是当前登录用户)
- 如果有权限,返回True,没有权限,返回False
- self.message 是给前端的提示信息
- 局部使用,全局使用,局部禁用
# 权限类
# 是超级管理员才能访问
from rest_framework.permissions import BasePermission
class UserTypePermission(BasePermission):
def has_permission(self, request, view):
# 只有超级管理员有权限
if request.user.user_type == 1:
return True # 有权限
else:
# self.message = '普通用户和2b用户都没有权限' # 返回给前端的提示是什么样
# 使用了choice后,user.user_type 拿到的是数字类型,想变成字符串 user.get_user_type_display()
# self.message = '您是:%s 用户,您没有权限'%request.user.get_user_type_display()
return False # 没有权限
频率类使用
可以对接口访问的频次进行限制,以减轻服务器压力。
使用步骤:
-
写一个类:继承SimpleRateThrottle
-
重写get_cache_key,返回唯一的字符串,会以这个字符串做频率限制
-
写一个类属性scop='随意写',必须要跟配置文件对象
-
配置文件中写
'DEFAULT_THROTTLE_RATES': { '随意写': '3/m' # 3/h 3/s 3/d } -
局部配置,全局配置,局部禁用
from rest_framework.throttling import BaseThrottle, SimpleRateThrottle
class MyThrottling(SimpleRateThrottle): # 我们继承SimpleRateThrottle去写,而不是继承BaseThrottle去写
# 类属性,这个类属性可以随意命名,但要跟配置文件对应
scope = 'luffy'
def get_cache_key(self, request, view):
# 返回什么,频率就以什么做限制
# 可以通过用户id限制
# 可以通过ip地址限制
return request.META.get('REMOTE_ADDR') # 取出访问者ip
# settings.py
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES':['app01.throttling.MyThrottling'],
'DEFAULT_THROTTLE_RATES': {
'luffy': '3/m' # key要跟类中的scop对应
}
}
认证源码分析
# 写个认证类,重写某个方法,配置在视图类上,就有认证了---》认证类加了,在视图类的方法中,request.user就是当前登录用户---》猜认证类的执行,是在在视图类的方法之前执行的
# 源码分析:
-之前咱们读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---》Request类中找user属性(方法),是个方法包装成了数据属性
-来到Request类中找:220行
def user(self):
if not hasattr(self, '_user'): # Request类的对象中反射_user
with wrap_attributeerrors():
self._authenticate() # 第一次会走这个代码
return self._user
-Request的self._authenticate()---》373行
def _authenticate(self):
for authenticator in self.authenticators: # 配置在视图类中所有的认证类的对象
try:
#(user_token.user, token)
user_auth_tuple = authenticator.authenticate(self) # 调用认证类对象的authenticate
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()
# 总结:认证类,要重写authenticate方法,认证通过返回两个值或None,认证不通过抛AuthenticationFailed(继承了APIException)异常
权限源码分析
-先读最简单的权限执行流程---》APIView的check_permissions(request),325行上下
def check_permissions(self, request):
for permission in self.get_permissions():
# permission是咱们配置在视图类中权限类的对象,对象调用它的绑定方法has_permission
# 对象调用自己的绑定方法会把自己传入(权限类的对象,request,视图类的对象)
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可以做什么用
简单读频率类源码
# 源码分析:
-之前咱们读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(到了频率的限制)
鸭子类型
# 走路像鸭子,说话像鸭子,它就是鸭子
# 指的是面向对中,子类不需要显示的继承某个类,只要有某个的方法和属性,那我就属于这个类
# 假设有个鸭子类Duck类,有两个方法,run,speak方法
# 假设又有一个普通鸭子类,PDuck,如果它也是鸭子,它需要继承Duck类,只要继承了鸭子类,什么都不需要写,普通鸭子类的对象就是鸭子这种类型;如果不继承,普通鸭子类的对象就不是鸭子这种类型
#假设又有一个唐老鸭子类,TDuck,如果它也是鸭子,它需要继承Duck类,只要继承了鸭子类,什么都不需要写,唐老鸭子类的对象就是鸭子这种类型;如果不继承,唐老鸭子类的对象就不是鸭子这种类型
# python不推崇这个,它推崇鸭子类型,指的是
不需要显示的继承某个类,只要我的类中有run和speak方法,我就是鸭子这个类
# 有小问题:如果使用python鸭子类型的写法,如果方法写错了,它就不是这个类型了,会有问题
# python为了解决这个问题:
-方式一:abc模块,装饰后,必须重写方法,不重写就报错
-方式二:drf源码中使用的:父类中写这个方法,但没有具体实现,直接抛异常
注意
# django的配置文件不要乱导入,乱导入可能会出错
'''
django的运行是在加载完配置文件后才能运行
因为模块的导入会执行那个模块,而这个模块中又有别的导入,别的导入必须djagno运行起来才能使用
'''
作业
1 编写图书和出版社的5个接口,所有接口都要有一分钟访问5次的频率限制
2 图书的接口需要登录才能方法,出版社的接口需要登录,并且是超级用户才能访问
models.py
from django.db import models
class Book(models.Model):
name = models.CharField(max_length=32)
price = models.CharField(max_length=32)
publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
def publish_detail(self):
return {'name': self.publish.name, 'address': self.publish.addr}
class Publish(models.Model):
name = models.CharField(max_length=32)
addr = models.CharField(max_length=32)
class User(models.Model):
username = models.CharField(max_length=32)
password = models.CharField(max_length=32)
user_type = models.IntegerField(choices=((1, '超级用户'), (2, '普通用户'), (3, '22用户')))
def __str__(self):
return self.username
class UserToken(models.Model):
user = models.OneToOneField(to='User', on_delete=models.CASCADE)
token = models.CharField(max_length=32, null=True)
serializer.py
from rest_framework import serializers
from .models import Book, Publish
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
# fields = '__all__'
fields = ['name', 'price', 'publish', 'publish_detail']
class PublishSerializer(serializers.ModelSerializer):
class Meta:
model = Publish
fields = '__all__'
views.py
import uuid
from django.shortcuts import render
from rest_framework.decorators import action
from rest_framework.generics import GenericAPIView, ListCreateAPIView, RetrieveUpdateDestroyAPIView
from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet
from rest_framework.mixins import RetrieveModelMixin, CreateModelMixin, UpdateModelMixin, DestroyModelMixin, \
ListModelMixin
from .permission import UserPermission
from .auth import LoginAuth
from .models import Book, User, UserToken, Publish
from .serializer import BookSerializer, PublishSerializer
class BookView(ModelViewSet):
serializer_class = BookSerializer
queryset = Book.objects.all()
class PublishView(ModelViewSet):
permission_classes = [UserPermission]
serializer_class = PublishSerializer
queryset = Publish.objects.all()
class UserView(ModelViewSet):
authentication_classes = []
@action(methods=['POST', ], detail=False, url_path='login')
def login(self, request):
username = request.data.get('username')
password = request.data.get('password')
user = User.objects.filter(username=username, password=password).first()
if user:
token = str(uuid.uuid4())
UserToken.objects.update_or_create(defaults={'token': token}, user=user)
return Response({'code': 100, 'msg': '登录成功', 'token': token})
else:
return Response({'code': 101, 'msg': '用户名或密码错误'})
auth.py
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from app01.models import UserToken
class LoginAuth(BaseAuthentication):
def authenticate(self, request):
# token = request.GET.get('token')
token = request.META.get('HTTP_TOKEN')
user_token = UserToken.objects.filter(token=token).first()
if user_token:
return user_token.user, token # 返回两个值,一个是当前登录用户,一个是token
else:
raise AuthenticationFailed('您没有登录')
permission.py
from rest_framework.permissions import BasePermission
class UserPermission(BasePermission):
message = '不是超级用户,查看不了'
def has_permission(self, request, view):
user_type = request.user.user_type
if user_type == 1:
return True
else:
return False
throttling.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')
urls.py
from django.contrib import admin
from django.urls import path, include
from rest_framework.routers import SimpleRouter
from app01 import views
router = SimpleRouter()
router.register('books', views.BookView, 'books')
router.register('publish', views.PublishView, 'publish')
router.register('user', views.UserView, 'user')
urlpatterns = [
path('admin/', admin.site.urls),
path('', include(router.urls)),
]
settings.py
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': ['app01.auth.LoginAuth', ],
'DEFAULT_THROTTLE_CLASSES': ['app01.throttling.MyThrottling', ],
# 'DEFAULT_THROTTLE_CLASSES': ['app01.throttling.OurThrottling', ],
'DEFAULT_THROTTLE_RATES': {
'luffy': '5/m'
}
}

浙公网安备 33010602011771号