Django REST framework - 认证、权限、频率

认证介绍和源码分析

1 只有认证通过的用户才能访问指定的url地址,比如:查询课程信息,需要登录之后才能查看,没有登录,就不能查看,这时候需要用到认证组件


2 APIVIew--->dispatche--->self.initial--->写的
    self.perform_authentication(request)# 认证
    self.check_permissions(request) # 权限
    self.check_throttles(request) # 频率
    
3 APIView的perform_authentication
    -request.user # 新的request对象,drf的Request类
    
4 Request类的user
    -被包装成了数据属性,内部有 self._authenticate()
    -Request类的_authenticate()方法
    
    
5 Request类的_authenticate()方法
    def _authenticate(self):
        for authenticator in self.authenticators:
            try:
                user_auth_tuple = authenticator.authenticate(self)
            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()
        
6 drf的Request对象实例化是再什么时候?
    -再APIVIew的dispatch最上面完成的
    -  return Request(
            request,
            parsers=self.get_parsers(),
            authenticators=self.get_authenticators(), # 看它
            negotiator=self.get_content_negotiator(),
            parser_context=parser_context
        )
7 APIView的get_authenticators
    def get_authenticators(self):
        return [auth() for auth in self.authentication_classes]
    -如果我再视图类中写:authentication_classes=[类名,类名1]
    -返回[对象,对象1]

认证类前奏登录功能,认证类编写

1 认证类的使用流程
    -写一个类,继承BaseAuthentication
    -在类中写authenticate(self, request):
    -在方法中进行校验,如果校验通过,返回两个值(返回空)
    -使用认证类,在视图类上加
    authentication_classes = [LoginAuth,]

先写两个表

##models.py 表模型
class User(models.Model):
    name = models.CharField(max_length=32)
    password = models.CharField(max_length=32)
    user_type = models.IntegerField(choices=((1, '超级管理员'), (2, '普通用户'), (3, '超级普通用户')), default=3)


class UserToken(models.Model):
    user = models.OneToOneField(to='User',on_delete=models.CASCADE)
    token=models.CharField(max_length=32)

views.py登录视图类

from rest_framework.viewsets import ViewSetMixin
from rest_framework.generics import ListAPIView, CreateAPIView
from app01 import models
from app01 import serializer
from rest_framework.decorators import action
from app01.response import APIResponse
import uuid
from app01.auth import LoginAuth

# 基于自己写的UserToken表版
class UserViews(ViewSetMixin, CreateAPIView):
    queryset = models.User.objects.all()
    serializer_class = serializer.UserSerializers

    @action(methods=['POST'], detail=False)
     def login(self, request):
       name = request.data.get('name')
       password = request.data.get('password')
       user = models.User.objects.filter(name=name, password=password).first()
       token = uuid.uuid4()  # 生成一个uuid的随机字符串
       # 这个是错误的:user.usertoken是None
       # user.usertoken.user=user
       # user.usertoken.token=token
       # 如果每次都是新增,如果它登录过,这个地方会报错
       # models.UserToken.objects.create(user=user,token=token)
       # 如果有就更新,如果没有就创建
       # 根据user去查询,如果能查到,就修改token,如果查不到,就新增一条
       models.UserToken.objects.update_or_create(defaults={'token': token}, user=user)
       if user:
           return APIResponse(msg='登录成功', token=token)
       else:
           return APIResponse(status=101, msg='用户名或密码错误')

urls.py 路由

from django.urls import path, include
from app01 import views
from rest_framework.routers import SimpleRouter

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

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

认证类

认证类的编写

###基于自己写的UserToken表
from rest_framework.exceptions import AuthenticationFailed
from app01 import models
from rest_framework.authentication import BaseAuthentication

class LoginAuth(BaseAuthentication):
    def authenticate(self, request):
        token = request.GET.get('token')
        user_token = models.UserToken.objects.filter(token=token).first()
        if user_token:
        # 登录了
        #返回两个值,第一个值,给了新的request对象的user属性,通常情况我们把当前登录用户给它
            return user_token.user, ''
        else:
            raise AuthenticationFailed('没有登陆')

使用认证类(全局用,局部用)

#全局用,setting中配置(所有接口都需要认证)
REST_FRAMEWORK={
    "DEFAULT_AUTHENTICATION_CLASSES":["app01.auth.LoginAuth",]
}
# 登录功能需要局部禁用,在视图类中加入
    authentication_classes = []
    
#只在局部使用,只在视图类中加
authentication_classes = [LoginAuth,]

权限类

编些权限类

from app01 import models
from rest_framework.permissions import BasePermission

class MyPermission(BasePermission):
    message='你没有权限'
    def has_permission(self, request, view):
        if request.user.user_type == 1:

            return True
        else:
            self.message='你是%s用户,你没有权限'%request.user.get_user_type_display()
            return False

权限类的使用

# 局部使用(在视图类中加)
permission_classes = [MyPermission,]
# 全局使用(在配置文件中配置)
REST_FRAMEWORK={
    "DEFAULT_PERMISSION_CLASSES":["app01.auth.MyPermission",],
}

频率类

定义一个频率类

from rest_framework.throttling import BaseThrottle, SimpleRateThrottle
class MyThrottle(SimpleRateThrottle):
    scope = 'ip_th'
    def get_cache_key(self, request, view):
        # 返回当前请求的ip地址
        return self.get_ident(request) 
        return request.Meta.get('REMOTE_ADDR')

        #第二种 按用户限制
        return request.user.id

局部使用,全局使用

# 局部用,在视图类中配置
throttle_classes = [MyThrottle,]
# 全局用,在配置文件中配置
REST_FRAMEWORK = {
    "DEFAULT_THROTTLE_CLASSES": ["app01.auth.MyThrottle", ],
    'DEFAULT_THROTTLE_RATES': {
        'ip_th': '5/m',  #IP一分钟访问5次
    },
}
posted @ 2021-12-06 17:21  沈忻凯  阅读(48)  评论(0)    收藏  举报