认证组件、权限组件、频率组件

发布时间 2023-09-06 17:25:39作者: 吼尼尼痛

认证组件

登录功能

写登录的时候,可以使用auth提供的user表,也可以自定义

===》自定义

写登录接口,登录成功,要有标志生成一个随机字符串,放在表中,以后他只要带这个随机字符串过来,我们就认为是这个人登录的

### ### ### ### ### ### ### 视图类### ### ### ### ### ### ### ### ### ### ### ### ###

from rest_framework.views import APIView
from rest_framework.generics import GenericAPIView
from rest_framework.viewsets import ViewSetMixin, ViewSet
from rest_framework.response import Response
from rest_framework.decorators import action
from .models import User, UserToken
import uuid


# class UserView(ViewSetMixin,APIView):
class UserView(ViewSet):
    @action(methods=['POST'], detail=False) # /user/login/    post 请求就会执行
    def login(self, request, *args, **kwargs):
        # 前端传入用户名密码
        username = request.data.get('username')
        password = request.data.get('password')
        user = User.objects.filter(username=username, password=password).first()

        if user:
            # 生成一个随机字符串,返回给前端,并且要把随机字符串存到token表中
            # 随机字符串使用uuid生成
            token = str(uuid.uuid4())
            # 把随机字符串存到token表中会有两种情况(如果之前没有登录过就是新增,如果之前登录过修改)
            # 先去UserToken表中,根据user查,如果能查到,就修改,查不到就新增一条记录
            ##### 方式一:麻烦方式
            # user_token=UserToken.objects.filter(user=user).first()
            # if user_token:
            #     user_token.token=token
            #     user_token.save()
            # else:
            #     UserToken.objects.create(user=user,token=token)

            ## 方式二:通过user去UserToken表中查,如果能查到用defaults的更新,如果查不到,就用user和defaults新增一条记录
            UserToken.objects.update_or_create(defaults={'token': token}, user=user)
            return Response({'code': 100, 'msg': '登录成功', 'token': token, 'username': user.username})

        else:
            return Response({'code': 101, 'msg': '用户名或密码错误'})

### ### ### ### ### ### ### 路由### ### ### ### 
from rest_framework.routers import SimpleRouter

router = SimpleRouter()
router.register('user', UserView, 'user')

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include(router.urls)),
]

认证组件

认证有什么作用?

  一些接口,想限制登录后才能访问,没登录不能访问

  做登录认证,限制如果没登录,不允许访问该接口

drf中,认证如何使用?

  1. 写一个类,继承BaseAuthentication

  2. 类中重写:authenticate

  3. 在authenticate完成登录认证, 如果登录了。返回两个值,如果没登录抛异常

  4. 在视图类中配置使用

 class BookView(ViewSet):
            authentication_classes = [LoginAuth]

如果登录了,在视图类的方法中,能拿出当前登录用户

  request.user

auth.py

from .models import UserToken

'''
按照如下步骤写
1. 写一个类,继承BaseAuthentication
2. 类中重写:authenticate方法
3. 在authenticate完成登录认证,如果登录了,返回两个值,如果没登录抛异常
4. 在视图类中配置事宜
'''

from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed

class LoginAuth(BaseAuthentication):
    def authenticate(self, request):
        # 如何知道当前请求这个人是登录了?
        # 拿到前端传入的token:当时给的随机字符串===》去UserToken表中查,如果能查到,说明就是这个人在访问,如果查不到,说明这个人没有登录过
        # 前端传入的token从哪拿?如何拿?===》后端定的:1 请求地址中  2. 请求体中  3. 请求头中
        token = request.query_params.get('token')
        user_token = UserToken.objects.filter(token=token).first()
        if user_token:  # 是登录状态
            return user_token.user, token  # 返回两个值,第一个是当前登录用户,第二个是前端token
        else:
            raise AuthenticationFailed('您没有登录')

视图类

from django.shortcuts import render

# Create your views here.

# 路由自动生成吗?    自动生成继承:ViewSetMixin   我想
# 要不要序列化,要不要跟数据库打交道,继承GenericAPIView:查出所有数据(只要一条),还要写个序列化类()
from rest_framework.views import APIView
from rest_framework.generics import GenericAPIView
from rest_framework.viewsets import ViewSetMixin, ViewSet
from rest_framework.response import Response
from rest_framework.decorators import action
from .models import User, UserToken
import uuid

from .auth import LoginAuth


# class UserView(ViewSetMixin,APIView):
class UserView(ViewSet):
    @action(methods=['POST'], detail=False)  # /user/login/    post 请求就会执行
    def login(self, request, *args, **kwargs):
        # 前端传入用户名密码
        username = request.data.get('username')
        password = request.data.get('password')
        user = User.objects.filter(username=username, password=password).first()

        if user:
            # 生成一个随机字符串,返回给前端,并且要把随机字符串存到token表中
            # 随机字符串使用uuid生成
            token = str(uuid.uuid4())
            # 把随机字符串存到token表中会有两种情况(如果之前没有登录过就是新增,如果之前登录过修改)
            # 先去UserToken表中,根据user查,如果能查到,就修改,查不到就新增一条记录
            ##### 方式一:麻烦方式
            # user_token=UserToken.objects.filter(user=user).first()
            # if user_token:
            #     user_token.token=token
            #     user_token.save()
            # else:
            #     UserToken.objects.create(user=user,token=token)

            ## 方式二:通过user去UserToken表中查,如果能查到用defaults的更新,如果查不到,就用user和defaults新增一条记录
            UserToken.objects.update_or_create(defaults={'token': token}, user=user)
            return Response({'code': 100, 'msg': '登录成功', 'token': token, 'username': user.username})

        else:
            return Response({'code': 101, 'msg': '用户名或密码错误'})


class BookView(ViewSet):
    authentication_classes = [LoginAuth]  # 这个认证类,管理了当前类下所有的方法

    def list(self, request):
        print(request.user.username)  # 当前登录用户
        return Response("你好:%s,你看到了好多书啊"%request.user.username)

路由

from rest_framework.routers import SimpleRouter

router = SimpleRouter()
router.register('user', UserView, 'user')
router.register('books', BookView, 'books')
urlpatterns = [
    path('', include(router.urls)),
]

小结

1. 认证类要写好,使用

  配置在视图类上===》局部使用

  配置文件中配置===》全局使用===》所有接口都必须登录后才能用

    REST_FRAMEWORK = {
      'DEFAULT_AUTHENTICATION_CLASSES': [
      'app01.auth.LoginAuth'
      ],
      }

    局部禁用:

    class UserView(ViewSet)

      authentication_class = []

2. 写的认证类:

  要重写authenticate方法,必须返回两个参数:当前登录用户:user_token.user,用户的token ===》后续在视图类中:request.user 就是认证类返回的第一个参数,request.auth 就是认证类返回的第二个参数

3. 如果认证失败,抛异常AuthenticationFailed,会被drf捕获,处理,不会报错到前端

4. 前端传入的token,从哪取?

  后端定的,我们这个项目是从请求地址中取

  还可以从请求头或者请求体中取

5. UserToken.objects.update_or_create

权限组件

系统中:有普通用户,超级用户,超级管理员,他们都登录了,又分权限,有的人有权限,就能访问这个接口,没权限,就不能访问

'''
使用步骤:
1. 写一个类,继承 BasePermission
2. 重写 has_permission
3. 在方法中校验用户是否有权限,如果有,就返回True,如果没有,就返回False
    由于他的执行是在认证之后,所有从request.user中取出当前的用户,判断权限
4. 在视图类中局部使用,在settings中全局使用,局部可以禁用'''
    class PublishView(ViewSet):
        permission_classes = [UserPermission] 

    REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'app01.auth.LoginAuth'
    ],
    'DEFAULT_PERMISSION_CLASSES': [
        'app01.permissions.UserPermission',
    ],
}

视图类

from rest_framework.permissions import BasePermission


# 1 写一个类,继承 BasePermission
# 2 重写 has_permission
# 3 在方法中校验用户是否有权限,如果有,就返回True,如果没有,就返回False
# 4

class UserPermission(BasePermission):

    def has_permission(self, request, view):
        # request 当次请求的request,  新的,它是在认证类之后执行的,如果认证通过了request.user 就是当前登录用户
        # 拿到当前登录用户,查看它的类型,确定有没有权限
        if request.user.user_type == 3:

            return True
        else:
            self.message = '您的用户类型是:%s,您没有权限操作' % (request.user.get_user_type_display())
            return False

补充:Django项目国际化

# 配置文件中--->以后所有英文都会转成中文
INSTALLED_APPS = [
    'rest_framework'
]

LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'
USE_I18N = True
USE_L10N = True
USE_TZ = False

频率组件

作用:控制一个接口一定时间段内的访问次数

对接口进行访问次数限制

# 使用步骤:
# 1. 写一个类,继承SimpleRateThrottle
# 2. 重写get_cache_key,返回什么,就以什么做限制:IP地址,用户id限制
3. 写一个类属性    scope = 'drf_day08'
# 4. 配置文件中配置
    '''
        'DEFAULT_THROTTLE_RATES': {
            'drf_day08': '3/m',  # 一分钟访问三次   5/s,m,h,d
        },
    '''
# 5. 局部使用,全局使用,局部禁用
class PublishView(ViewSet):
        throttle_classes = [IPRateThrottle]

# 6 全局使用
         'DEFAULT_THROTTLE_CLASSES': ['app01.throttling.IPRateThrottle'],

视图类

from rest_framework.throttling import SimpleRateThrottle


# 1 写一个类,继承SimpleRateThrottle
# 2 重写get_cache_key,返回什么,就以什么做限制: IP地址,用户id限制
# 3 写一个类属性   scope = 'drf_day08'
# 4 配置文件中配置
'''
    'DEFAULT_THROTTLE_RATES': {
        'drf_day08': '3/m',  # 一分钟访问三次
    },
'''

# 5 局部使用,全局使用,局部禁用


class IPRateThrottle(SimpleRateThrottle):
    scope = 'drf_day08'  # 写一个类属性
    def get_cache_key(self, request, view):
        # 返回ip,以ip地址限制
        print(request.META)
        return request.META.get('REMOTE_ADDR')

排序

只有5个接口中的查询所有,才涉及到排序

使用步骤:

  1. 必须写在继承:GenericAPIView类的视图类中才行

  2. 配置类属性:

filter_backends = [OrderingFilter]
ordering_fields = ['id', 'user_tyoe']  # 可以排序的字段

  3. 使用:

  http://127.0.0.1:8000/user/?ordering=user_type #用户类型升序排
  http://127.0.0.1:8000/user/?ordering=-user_type #用户类型降序排
  http://127.0.0.1:8000/user/?ordering=user_type,-id#先按用户类型升序排,如果用户类型一样,再按id降序排