drf- 三大认证、排序

发布时间 2023-09-10 10:27:48作者: Way*yy

三大认证组件

登录认证

我们可以使用auth自带的User表,也可以选择自定义表.

写登录接口,登录成功要有标志,生成一个随机的字符串,放到表中,以后只要携带这个字符串就可以登录成功。
视图层
方法一:
class UserView(GenericViewSet):
    queryset = models.UserInfo.objects.all()
    serializer_class = UserSerializer

    def login(self, request, *args, **kwargs):
        # 获取从前端传入的数据,并进行数据校验判断该用户是否存在,拿到的是QuerySet对象
        user_obj = self.get_queryset().filter(username=request.data.get("username"),password=request.data.get("password"))
        print(user_obj[0])
        # 判断用户是否存在
        if user_obj:
            # 存在获取一个UUID
            token = str(uuid.uuid4())
            # 通过用户对象查询Token表中是否存在一个Token对象,返回None或者一个对象
            user_taken = models.Token.objects.filter(usertoken=user_obj[0]).first()
            print(token, "++++++++")  # c03d3613-d4b7-4c17-abf6-eb82809fcfee ++++++++
            print(user_taken, "----------")  # Token object (1) ----------
            # 判断这个Token对象是否存在
            if user_taken:
                # 如果存在将得到的UUID的对象值赋值给Token表中的Token字段,后续可以使用这个字段开登录
                models.Token.token = token
                # 保存
                user_taken.save()
            else:
                # 如果这个Token对象不存在则给它添加一个
                models.Token.objects.create(token=token, usertoken_id=user_obj[0].pk)
            return Response({"code": 100, "msg": "登录成功", "username": user_obj[0].username, "token": token})
        else:
            return Response({"code": 101})
        
方法二:
	使用UserToken.objects.update_or_create(defaults={'token': token}, user=user)

class UserView(GenericViewSet):
    queryset = models.UserInfo.objects.all()
    serializer_class = UserSerializer

    def login(self, request, *args, **kwargs):
        # 获取从前端传入的数据,并进行数据校验判断该用户是否存在,拿到的是QuerySet对象
        user_obj = self.get_queryset().filter(username=request.data.get("username"),password=request.data.get("password"))
        print(user_obj[0])
        # 判断用户是否存在
        if user_obj:
            # 存在获取一个UUID
            token = str(uuid.uuid4())
            Token.objects.update_or_create(defaults={"token": token}, usertoken=user_obj[0])
            return Response({"code": 100, "msg": "登录成功", "username": user_obj[0].username, "token": token})
        else:
            return Response({"code": 101, "msg": "账号或密码错误,请重新输入"})
路由层
urlpatterns = [
    path('register/', views.UserView.as_view({"post": "register"})),
    path('login/', views.UserView.as_view({"post": "login"})),

认证组件

认证的的作用:
	有些接口需要登陆以后才可以访问,没有登录的话就不能访问
    登录的接口就是为了限制某些接口的
    
认证类的使用:
	1、导入模块:from rest_framework.authentication import BaseAuthentication
    2、重写里面的    def authenticate(self, request):方法
    3、在authenticate中完成登录的认证,如果登录成功返回两个值,如果没登录抛出异常
    4、在视图类中局部使用
    
如果已经登录成功,可以在视图类中通过request.user拿到当前登录用户
使用方法
auth.py
1、新建一个auth.py文件用来专门做认证
2、在auth文件中写一个CBV继承BaseAuthentication,在里面重写def authenticate(self, request):方法
from rest_framework.authentication import BaseAuthentication
from .models import UserToken
from rest_framework.exceptions import AuthenticationFailed


class AuthUser(BaseAuthentication):
    def authenticate(self, request):
        # 获取从前端传入的token值
        token = request.GET.get("token")
        # 根据获得的token值查token表
        token_obj = UserToken.objects.filter(token=token).first()
        # 如果存在返回用户名以及token值
        if token_obj:
            return token_obj.user, token
        # 否则抛出异常
        else:
            raise AuthenticationFailed("您还没有登录")
视图层
from rest_framework.viewsets import ViewSet
from .models import User, UserToken
from rest_framework.response import Response
from rest_framework.decorators import action
from .auth import AuthUser
import uuid


# Create your views here.
class UserView(ViewSet):
    @action(methods=["POST"], detail=False)
    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()
        print(user)
        if user:
            token = str(uuid.uuid4())
            UserToken.objects.update_or_create(defaults={"token": token}, user=user)
            return Response({"code": 100, "msg": "登录成功", "username": user.username, "token": token})
        else:
            return Response({"code": 101, "msg": "登录失败"})


class PublishView(ViewSet):
    # 如果想要访问此视图函数必须先登录认证
    authentication_classes = [AuthUser] 

    def list(self, request):
        return Response("hhhh")
路由层
from django.contrib import admin
from django.urls import path, include
from app_one.views import UserView, PublishView
from rest_framework.routers import SimpleRouter

render = SimpleRouter()
render.register("user", UserView, "user")
render.register("publish", PublishView, "publish")

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

小结
 1、先写好认证类
    -局部使用------>配置在视图类中:authentication_classes = [AuthUser]
    -全局使用:
    REST_FRAMEWORK = {
        'DEFAULT_AUTHENTICATION_CLASSES': [
            'app01.auth.LoginAuth'
        ],
    }
    -局部禁用------>在视图类中配置:authentication_classes = []

写认证类:
	-一定要重写authenticate方法,否则会抛异常,必须要返回两个参数:当前登录用户user_token.user,用户的 token
    -认证类返回的第一个参数---->request.user
    -认证类返回的第二个参数---->request.auth
    
    -认证失败主动抛出异常抛AuthenticationFailed,会被drf捕捉到并做处理,不会在前端报错
    
    -前端传入的token值从哪里取?
    	-在url中:request.GET()/request.query_params.get()
        -请求头:request.META.get('HTTP_TOKEN')属性来获取请求头中的数据
        -请求体:request.data

权限组件

在系统中:有超级用户、有普通用户、有管理员用户,他们都登陆过了,有分权限,就像普通用户只能访问部分功能, 超级管理员则是可以访问全部功能
使用步骤
from rest_framework.permissions import BasePermission
1、写一个类,继承BasePermission
2、重写has_permission
3、在方法中校验用户是否有权限,如果有,就返回True,如果没有,就返回False
	-由于它的执行是在认证之后,所有从request.user中取出当前等用户,判断权限
    
4、局部使用,在视图类中----->permission_classes = [Permissions]

5、全局使用,在配置文件中:
    REST_FRAMEWORK = {
        'DEFAULT_AUTHENTICATION_CLASSES': [
            'app01.auth.LoginAuth'
        ],
        'DEFAULT_PERMISSION_CLASSES': [
            'app01.permissions.Permissions',
        ],
}
permission类
from rest_framework.permissions import BasePermission

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

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

        else:
            self.message = "您的用户类型是%s,您没有权限访问该功能" % (request.user.get_user_type_display())
            return False
视图类
class PublishView(ViewSet):
    # 如果未登录无法访问该接口
    authentication_classes = [AuthUser]
    # 如果权限不够无法访问该接口
    permission_classes = [Permissions]

    def list(self, request):
        return Response("hhhh")
路由类
from rest_framework.routers import SimpleRouter

render = SimpleRouter()
render.register("user", UserView, "user")
render.register("publish", PublishView, "publish")

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

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 = 'XXX'
     4、在配置文件中进行配置:
        '''
            'DEFAULT_THROTTLE_RATES': {
                'drf_day08': '3/m',  # 一分钟访问三次   		5/s,m,h,d,/秒/分/时/天
            },
        '''
        
    5、局部使用、全局使用,局部禁用:
    	-局部使用:throttle_classes = [IPRateThrottle]
        -全局使用:
        'DEFAULT_THROTTLE_CLASSES': ['app01.throttling.IPRateThrottle'],
throttle
from rest_framework.throttling import SimpleRateThrottle


class IPRateThrottle(SimpleRateThrottle):
    # 必须要自定义一个属性,值可以任意命名
    scope = "Wayyy"
	# 重写get_cache_key方法
    def get_cache_key(self, request, view):
        return request.META.get("REMOTE_ADDR")
配置文件中配置
'DEFAULT_THROTTLE_RATES': {
    'Wayyy': '3/m',  # 一分钟访问三次
},
视图文件
class PublishView(ViewSet):
    # 如果未登录无法访问该接口
    authentication_classes = [AuthUser]
    # 如果权限不够无法访问该接口
    permission_classes = [Permissions]

    throttle_classes = [IPRateThrottle]

    def list(self, request):
        return Response("hhhh")

排序

什么时候用?
	涉及到查询所有数据的时候
 
# 使用步骤
    - 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从 请求头中取 ----->request.META
    -token从 请求体中取 ----->request.data.get("tokenk")
    
# 2 编写权限类
# 4 编写频率类,以用户id限制,一分钟只能访问5次

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

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

编写books5个接口,所有接口必须登录后才能访问,限制普通登录用户只能查看所有和新增一条,超级用户能查看一条,删除,修改
视图类
class CatBookView(ViewSetMixin, ListCreateAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializers
    authentication_classes = [AuthUser]
    permission_classes = [Permissions]
    throttle_classes = [IPRateThrottle]


class UpdateBookView(ViewSetMixin, RetrieveUpdateDestroyAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializers
    authentication_classes = [AuthUser]
    permission_classes = [Permissions]
    throttle_classes = [IPRateThrottle]
auth类
from rest_framework.authentication import BaseAuthentication
from .models import UserToken
from rest_framework.exceptions import AuthenticationFailed


class AuthUser(BaseAuthentication):
    def authenticate(self, request):
        token = request.GET.get("token")
        # print(request.META)
        # print(request.headers)
        token_obj = UserToken.objects.filter(token=token).first()
        if token_obj:
            return token_obj.user, token
        else:
            raise AuthenticationFailed("您还没有登录")
permissions类
from rest_framework.permissions import BasePermission


class Permissions(BasePermission):
    def has_permission(self, request, view):
        # print(request.__dict__)
        if request.user.user_type == 3:
            return True

        else:
            self.message = "您的用户类型是%s,您没有权限访问该功能" % (request.user.get_user_type_display())
            return False

throttle类
from rest_framework.throttling import SimpleRateThrottle


class IPRateThrottle(SimpleRateThrottle):
    scope = "Wayyy"

    def get_cache_key(self, request, view):
        token = request.GET.get("token")
        user_id = request.user.pk
        return f"{self.scope}:{token}:{user_id}"
序列化类
from rest_framework import serializers
from .models import Book


class BookSerializers(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = "__all__"
路由层
from django.contrib import admin
from django.urls import path, include
from app_one.views import CatBookView, UpdateBookView
from rest_framework.routers import SimpleRouter

render = SimpleRouter()
render.register("books", CatBookView, "books")
render.register("book", UpdateBookView, "book")

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