drf之三大组件(认证组件、权限组件、频率组件)

发布时间 2023-04-26 18:17:51作者: 树苗叶子

环境准备

创建相关的表,models.py

class User(models.Model):
    username = models.CharField(max_length=32)
    password = models.CharField(max_length=32)


class auth(models.Model):
    token = models.CharField(max_length=64, unique=True)
    user = models.OneToOneField(to='User', on_delete=models.CASCADE, null=True)

创建登录样例

需求:当用户登录后,自动生成一个token随机字符串,并写入到数据库中。

需要使用两个新功能:

  • import uuid 可以生成一个随机字符串
  • orm的update_or_create
    • update_or_create作用是在数据库中执行更新或插入操作。它可以根据给定的筛选条件来查找一条记录,如果找到则更新该记录,否则插入一条新记录。
    • 其中,defaults 参数用于指定需要更新或插入的数据,它是一个字典类型;**kwargs 参数用于指定查找记录所需要的筛选条件,通常是字段名和字段值的键值对形式。
    • 需要注意的是,如果要使用 update_or_create 方法,必须定义一个唯一索引或唯一约束来确保更新或插入的数据不会与现有数据冲突。否则在插入时会抛出唯一性冲突的异常。
from app01 import models
from app01.serializer.serializer import BookModelSerializer
from rest_framework.viewsets import ModelViewSet, ViewSet
from rest_framework.response import Response
from rest_framework.decorators import action
class UserView(ViewSet):
    @action(methods=['POST'], detail=False)
    def login(self, request):
        username = request.POST.get('username')
        password = request.POST.get('password')
        user_obj = models.User.objects.filter(username=username, password=password).first()
        if user_obj:
            print('登录成功')
            # 如果登录成功,则生成一个随机字符串-uuid
            import uuid
            # 生成一个“永不重复”的随机字符串
            token = str(uuid.uuid4())
            # update_or_create作用是在数据库中执行更新或插入操作。它可以根据给定的筛选条件来查找一条记录,如果找到则更新该记录,否则插入一条新记录。
            # 其中,defaults 参数用于指定需要更新或插入的数据,它是一个字典类型;**kwargs 参数用于指定查找记录所需要的筛选条件,通常是字段名和字段值的键值对形式。
            # 需要注意的是,如果要使用 update_or_create 方法,必须定义一个唯一索引或唯一约束来确保更新或插入的数据不会与现有数据冲突。否则在插入时会抛出唯一性冲突的异常。
            models.Auth.objects.update_or_create(user=user_obj, defaults={'token': token})
            return Response({'code': 100, 'msg': '恭喜!!登录成功!'})
        else:
            print('用户名或密码错误')
            return Response({'code': 101, 'msg': '登录失败,用户名或密码错误'})

配置路由

from rest_framework.routers import SimpleRouter
login_route = SimpleRouter()
login_route.register('', views.UserView, '')
urlpatterns = [
    path('', include(login_route.urls))
]

认证组件BaseAuthentication

需求:添加登录限制,写一个接口,当用户登录后就可以访问,未登录则不能访问。

创建认证组件

  • 新建一个认证用py文件,名字随意,我这里叫permission.py
  • 在permission.py中创建认证类
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from app01 import models
class LoginAuth(BaseAuthentication):
    def authenticate(self, request):
        # 在这里写认证逻辑,如果是登录过的,则可以访问,否则报错
        # 判断请求中是否有token,判断是否登录
        token = request.META.get('HTTP_AUTHORIZATION', None)
        # 判断前端是否传入了token
        if token:
            # 前端传入了token后,则去数据库中查询是否存在
            user_token = models.Auth.objects.filter(token=token).first()
            if user_token:
                # 正常需要返回两个值,[当前登录用户,token]
                return user_token.user, token
            else:
                raise AuthenticationFailed('用户认证失败')
        else:
            raise AuthenticationFailed('用户未登录,请先登录')

创建测试样例

  1. 用户登录后才可以查询所有
  2. 用户未登录只能按主键查询
from app01 import models
from app01.serializer.serializer import BookModelSerializer
from rest_framework.viewsets import ViewSet, ViewSetMixin
from rest_framework.generics import RetrieveAPIView, ListAPIView
from rest_framework.response import Response
from app01.permission import LoginAuth  # 这个是自己创建的认证类


class BookView(ViewSetMixin, ListAPIView):
    queryset = models.Book.objects.all()
    serializer_class = BookModelSerializer
	# 在视图中使用认证类
    authentication_classes = [LoginAuth]


class BookDetailView(ViewSetMixin, RetrieveAPIView):
    queryset = models.Book.objects.all()
    serializer_class = BookModelSerializer

验证如下图,在未使用token和使用错误token时都会报错,输入了正确的token后,可以正常访问数据。
image

总结

1. 创建认证组件

- 新建一个认证用py文件,名字随意,我这里叫permission.py
- 在permission.py中创建认证类,继承BaseAuthentication类并重写authenticate方法
	- 在permission的认证类中,如果认证成功需要返回None或两个值,第一个是登录的用户,第二个是token
	- 在permission的认证类中,如果认证不通过,使用AuthenticationFailed抛异常

2. 局部使用(只在一个视图类中使用,使用后会影响当前视图类中管理的所有接口)

- 导入自己写的认证类
- 在视图类中使用authentication_classes = [LoginAuth]进行调用

3. 全局使用(对所有接口都生效)

- 一般与局部禁用搭配使用,因为登录接口不应该认证
- 配置方法
# 这个配置可以在drf的settings.py中找到(C:\app\Python38\Lib\site-packages\rest_framework\settings.py)
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'app01.permission.LoginAuth'
    ],
}

4. 局部禁用

- 当配置了全局使用后,需要在一个视图类中禁用,禁用方法只要把authentication_classes = [] 定义为空值即可
class BookView(ViewSetMixin, ListAPIView):
    queryset = models.Book.objects.all()
    serializer_class = BookModelSerializer
    # 局部禁用,只要把下面的配置配置为空值即可
    authentication_classes = []

权限组件BasePermission

需求:实现定义三种用户:超级用户、普通用户、非法用户等

环境准备

在上面的环境的基础上,将用户表添加一个控制用户类型的字段,如下:

class User(models.Model):
    username = models.CharField(max_length=32)
    password = models.CharField(max_length=32)
    user_type = models.IntegerField(choices=((1, '超级管理员'), (2, '普通用户'), (3, '非法用户')), default=2)

权限组件

  1. 创建控制权限类的py文件,文件名自定义
  2. 在文件中定义控制权限类
  3. 在权限类中重写has_permission(self, request, view)方法,实现权限控制
  4. 如果有权限return True,如果没有权限return False

创建权限组件

from rest_framework.permissions import BasePermission
class CustomPermission(BasePermission):
    def has_permission(self, request, view):
        if request.user.user_type == 1:
            # 通过get_user_type_display()方法可获取到choice对应的中文
            self.message = '欢迎%s' % request.user.get_user_type_display()
            return True
        else:
            # 通过get_user_type_display()方法可获取到choice对应的中文
            self.message = '您是%s,无法访问' % request.user.get_user_type_display()
            return False

创建测试样例

from app01 import models
from app01.serializer.serializer import BookModelSerializer
from rest_framework.viewsets import ViewSet, ViewSetMixin
from rest_framework.generics import RetrieveAPIView, ListAPIView
from rest_framework.response import Response
from app01.permission import LoginAuth, CustomPermission


class BookView(ViewSetMixin, ListAPIView):
    queryset = models.Book.objects.all()
    serializer_class = BookModelSerializer
    authentication_classes = [LoginAuth]
    # 使用permission_classes来开启权限认证
    permission_classes = [CustomPermission]

class BookDetailView(ViewSetMixin, RetrieveAPIView):
    queryset = models.Book.objects.all()
    serializer_class = BookModelSerializer

总结

  • 当用户使用token登录后,可以使用request.user拿到用户信息
  • get_user_type_display()方法可获取到choice对应的中文

1. 创建认证组件

- 在文件中定义控制权限类
- 在权限类中重写has_permission(self, request, view)方法,实现权限控制
- 如果有权限return True,如果没有权限return False

2. 局部使用(只在一个视图类中使用,使用后会影响当前视图类中管理的所有接口)

- 导入自己写的认证类
- 在视图类中使用permission_classes = [自定义的权限组件]进行调用

3. 全局使用(对所有接口都生效)

- 一般与局部禁用搭配使用,因为登录接口不应该认证
- 配置方法
# 这个配置可以在drf的settings.py中找到(C:\app\Python38\Lib\site-packages\rest_framework\settings.py)
REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': [
        'app01.permission.CustomPermission',
    ],
}

4. 局部禁用

- 当配置了全局使用后,需要在一个视图类中禁用,禁用方法只要把authentication_classes = [] 定义为空值即可
class BookView(ViewSetMixin, ListAPIView):
    queryset = models.Book.objects.all()
    serializer_class = BookModelSerializer
    # 局部禁用,只要把下面的配置配置为空值即可
    permission_classes = []

频率组件throttling

  • 频率组件可以控制用户的访问频率,比如每分钟可以发起多少次请求
  • 可以按IP或者用户ID进行频率访问控制
    • 获取用户的IP:request.META.get('REMOTE_ADDR')
    • 获取用户的ID:request.user.pk

创建认证组件

  • 重写get_cache_key(self, request, view)方法
  • 定义scope变量,配置认证组件时使用
  1. 创建认证组件
class CustomThrottle(SimpleRateThrottle):
    scope = 'mythrottle'

    def get_cache_key(self, request, view):
        return request.META.get('REMOTE_ADDR')
  1. 在settings.py中注册
REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_RATES': {
        'mythrottle': '5/m',    # 这里是scope定义的名字,5/m是每分钟允许访问5次
    },
}

创建测试样例

from app01 import models
from app01.serializer.serializer import BookModelSerializer
from rest_framework.viewsets import ViewSet, ViewSetMixin
from rest_framework.generics import RetrieveAPIView, ListAPIView
from rest_framework.response import Response
from app01.permission import LoginAuth, CustomPermission
# 导入创建的频率限制类
from app01.throttling import CustomThrottle


class BookView(ViewSetMixin, ListAPIView):
    queryset = models.Book.objects.all()
    serializer_class = BookModelSerializer
    authentication_classes = [LoginAuth]
    permission_classes = [CustomPermission]
    # 导入相关规则
    throttle_classes = [CustomThrottle]

总结

  • 可以使用request.META.get('REMOTE_ADDR')拿到用户IP信息

1. 创建频率类

- 继承SimpleRateThrottle
- 配置一个类属性:scope = '注册名'
- 在权限类中重写get_cache_key(self, request, view)方法,实现权限控制
- 在settings.py中的REST_FRAMEWORK中进行注册,注册名由scope定义
- 如果需要定制返回错误信息,self.message = '错误信息'  即可

2. 局部使用(只在一个视图类中使用,使用后会影响当前视图类中管理的所有接口)

- 导入自己写的认证类
- 在视图类中使用 throttle_classes = [自定义的权限组件]

3. 全局使用(对所有接口都生效)

- 一般与局部禁用搭配使用,因为登录接口不应该认证
- 配置方法
REST_FRAMEWORK = {
    # 注册
    'DEFAULT_THROTTLE_RATES': {
        'mythrottle': '5/m',    # 这里是scope定义的名字
    },
    # 全局使用
    'DEFAULT_THROTTLE_CLASSES': ['app01.throttling.CustomThrottle'],
}
> ### 局部禁用
	- 当配置了全局使用后,需要在一个视图类中禁用,禁用方法只要把 throttle_classes = [] 定义为空值即可
```python
class BookView(ViewSetMixin, ListAPIView):
    queryset = models.Book.objects.all()
    serializer_class = BookModelSerializer
    # 局部禁用,只要把下面的配置配置为空值即可
    throttle_classes = []