认证组件,权限组件,频率组件,排序

发布时间 2023-09-06 16:34:42作者: 毓见

1 认证组件

1.1 登录功能

 写登录的时候,可以使用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': '用户名或密码错误'})

回顾:

UserToken表中有user字段
    拿到了一个UserToken表的对象  
    	user_token.token 就是字符串
        user_token.user  基于对象的跨表查询,拿到的是user对象  user_token.user.password
        user_token.user_id  隐藏了这个字段,是可以用的,它是管理的user对象的id号
    查询功能
     UserToken.objects.filter(user=user对象)
     UserToken.objects.filter(user_id=user.id)

路由

from rest_framework.routers import SimpleRouter

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

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

1.2 认证组件

APIView执行流程

-在视图视图类的方法之前,执行了三大认证

认证有什么作用?

-一些接口,想限制登录后才能访问,没登录不能访问
-做登录认证,限制如果没登录,不允许访问该接口

drf中,认证类如何使用

  • 1 写一个类,继承BaseAuthentication ?
  • 2 类中重写 :authenticate方法 ?‍♀️
  • 3 在authenticate方法中完成登录认证,如果登录了,返回两个值,如果没登录抛异常 ?
  • 4 在视图类中配置使用 ?
class BookView(ViewSet):
    	authentication_classes = [LoginAuth]        

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

request.user
  • 全局使用后,局部禁用 ?

    class UserView(ViewSet):
    # 局部禁用
    authentication_classes = []

  • 认证类的使用顺序 ?

  1. -优先用视图类配置的
  2. -其次用项目配置文件
  3. -最后用drf默认的配置

1.2.1 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('您没有登录')

1.2.2 视图类

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)

1.2.3 路由

from rest_framework.routers import SimpleRouter

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

1.3 小结

 1 认证类写好,使用
	-配置在视图类上---》局部使用
    -配置文件中配置--》全局使用---》所有接口都必须登录后才能用
        REST_FRAMEWORK = {
        'DEFAULT_AUTHENTICATION_CLASSES': [
            'app01.auth.LoginAuth'
        	],
    	}
    -局部禁用:
    class UserView(ViewSet):
    	authentication_classes = []
 2 写的认证类:要重写authenticate方法,必须返回两个参数:当前登录用户:user_token.user,用户的 token----》后续再视图类中:request.user  就是认证类返回的第一个参数,request.auth 就是认证类返回的第二个参数

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

 4 前端传入的token,从哪取?
	-后端定的,我们这个项目是从请求地址中取
    -还可以从请求头或请求体中取
 5 UserToken.objects.update_or_create

2 权限组件

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


# 使用步骤:
 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

2.1 django项目国际化

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

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

3 频率组件

 控制一个接口,访问频次,比如一分钟只能访问1次
 对接口进行访问次数限制
  • 使用步骤:?
  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 排序

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

 查询所有用户---》接口

 使用步骤
	- 1 必须写在继承:GenericAPIView 类的视图类中才行
    -2 配置类属性:
        filter_backends = [OrderingFilter]
    	ordering_fields=['id','user_type'] #可以排序的字段
        
    -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降序排
 1 认证类
	-token从 请求头中取
    -token从 请求体中取
    
 2 编写权限类
 3 编写频率类,以用户id限制,一分钟只能访问5次

 4 编写books5个接口,所有接口必须登录后才能访问,限制普通登录用户只能查看所有和新增一条,超级用户能查看一条,删除,修改

 5 对查询所有图书接口可以按price和id排序