首页 > 编程语言 >drf三大认证(认证,权限,频率)及其源码分析

drf三大认证(认证,权限,频率)及其源码分析

时间:2022-10-09 19:22:44浏览次数:54  
标签:self request 认证 token 源码 user import 三大

  • drf三大认证之认证

  • drf三大认证之权限

  • drf三大认证之频率

  • drf三大认证之认证源码分析

  • drf三大认证之权限源码分析

  • 鸭子类型


drf三大认证之认证

  访问接口,必须登录后才能访问

通过认证类完成,使用步骤
    1 写一个认证类,继承BaseAuthentication
    2 重写authenticate方法,在内部做认证
    3 如果认证通过,返回2个值
    4 认证不通过抛AuthenticationFailed异常
    5 只要返回了两个值,在后续的request.user 就是当前登录用户

auth.py

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


class LoginAuth(BaseAuthentication):
    def authenticate(self, request):
        # 在这里做认证,校验用户是否登录(带了token,并且能查到,就是登录,返回两个值,否则就是没登录,抛异常)
        # 用户带的token从哪取?后端人员定的:放在请求地址中
        token = request.GET.get('token')
        # 通过token查询该token是否是在表中有记录
        user_token = UserToken.objects.filter(token=token).first()
        if user_token:
            return user_token.user, token  # 返回两个值,一个是当前登录用户,一个是token
        else:
            raise AuthenticationFailed('您没有登录')

 如果想让某个视图类登录后才能访问
        -方式一:在视图类中定义authentication_classes
           class BookView(ModelViewSet):
         authentication_classes = [LoginAuth,]


        -方式二:全局配置
            REST_FRAMEWORK={
            'DEFAULT_AUTHENTICATION_CLASSES':['app01.auth.LoginAuth',]
        }

            
         -局部禁用:(配合全局配置使用,如果不想让改视图类经过认证,则给他一个空列表)
            authentication_classes = []

在前端访问的时候携带token可以在请求头(/users/?token=带token的随机字符串),或者请求体中

drf三大认证之权限

步骤:

  1. 导入BasePermission这个类

  from rest_framework.permissions import BasePermission

  2. 写一个类,继承BasePermission

   class UserTypePermission(BasePermission):

  3. 重写has_permisson方法

  def has_permission(self, request, view):

  4. 再方法中校验用户是否有权限(request.user就是当前登录的用户)--因为在登录认证的代码中有一个返回值为user_token.user

  if request.user.user_type == 1:

  5. 如果第四步行得通也就是有权限那么就返回True,否则返回False

     return True
   else:
       return False

  6. 我们也可以用self.massage给前端传去提示信息

    自定义提示信息

      1. self.message = '普通用户和2b用户都没有权限' # 返回给前端的提示是什么样

      2. self.message = '您是:%s 用户,您没有权限'%request.user.get_user_type_display()

        使用了choice后,user.user_type 拿到的是数字类型,想变成字符串 user.get_user_type_display()   _display()就是去拿choice(1,'超级管理员')中的超级管理员这个值

      3. self.message = f'你是{request.user.get_user_type_display()},你怎么敢访问这里的'

ps:  在源码中有这么一句话

  Return `True` if permission is granted, `False` otherwise

  如果授予许可,则返回' True ',否则返回' False '  

  7. 局部使用  全局使用  局部禁用

  局部使用

from .permission import UserTypePermission
class PublishView(ModelViewSet):
    permission_classes = [UserTypePermission]

  全局使用

REST_FRAMEWORK = {
    # 权限管理
    'DEFAULT_PERMISSION_CLASSES': ['app01.permission.UserTypePermission'],
}

  局部禁用

class PublishView(ModelViewSet):
    permission_classes = [ ]

drf三大认证之频率

限制访问该视图类的次数,比如一分钟三次(3/m  4/h  5/s  6/d)

步骤

  1. 导入SimpleRateThrottle这个类

  from rest_framework.throttling import SimpleRateThrottle

  2. 写一个类,继承SimpleRateThrottle

  class MyThrottling(SimpleRateThrottle):

  3. 重写get_cache_key,返回唯一的字符串,会以这个字符串做频率限制

   def get_cache_key(self, request, view):
        # 返回什么,频率就以什么做限制
        # 可以通过用户id限制
        # 可以通过ip地址限制
        return request.META.get('REMOTE_ADDR')

  4. 配置scope='随便写'  随便写必须要和配置文件中的对应

  scop='随意写'

  5. 在配置文件中写

  'DEFAULT_THROTTLE_RATES': {
        '随意写': '3/m'  # 3/h  3/s  3/d
        }

  6. 局部使用  全局使用  局部禁用

  局部使用

from .throttling import UserThrottling
class BookView(ModelViewSet):
    throttle_classes = [UserThrottling,]

  全局使用

REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_CLASSES':['app01.throttling.MyThrottling'],
    'DEFAULT_THROTTLE_RATES': {
        'luffy': '3/m'
    }
}

  局部禁用

class BookView(ModelViewSet):
    throttle_classes = []

drf三大认证之认证源码分析

# 写个认证类,重写某个方法,配置在视图类上,就有认证了---》认证类加了,在视图类的方法中,request.user就是当前登录用户---》猜认证类的执行,是在在视图类的方法之前执行的
# 源码分析:
	-之前咱们读APIView的执行流程---》包装了新的request,执行了3大认证,执行视图类的方法,处理了全局异常
    -入口:APIView的dispatch
    -APIView的dispatch的496行上下:self.initial(request, *args, **kwargs)
    -APIView的initial
    -413行上下:有三句话,分别是:认证,权限,频率
    	self.perform_authentication(request)
        self.check_permissions(request)
        self.check_throttles(request)
    -读认证类的源码---》APIView的perform_authentication(request),315行上下
    	def perform_authentication(self, request):
        	request.user  # 新的request
    -request是新的request---》Request类中找user属性(方法),是个方法包装成了数据属性
	-来到Request类中找:220行
       def user(self):
            if not hasattr(self, '_user'): # Request类的对象中反射_user
                with wrap_attributeerrors():
                    self._authenticate()  # 第一次会走这个代码
            return self._user
    -Request的self._authenticate()---》373行
        def _authenticate(self):
            for authenticator in self.authenticators: # 配置在视图类中所有的认证类的对象 
                try:
                    #(user_token.user, token)
                    user_auth_tuple = authenticator.authenticate(self) # 调用认证类对象的authenticate
                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()
            
            
    # 总结:认证类,要重写authenticate方法,认证通过返回两个值或None,认证不通过抛AuthenticationFailed(继承了APIException)异常

drf三大认证之权限源码分析

-先读最简单的权限执行流程---》APIView的check_permissions(request),325行上下
    def check_permissions(self, request):
        for permission in self.get_permissions():
            # permission是咱们配置在视图类中权限类的对象,对象调用它的绑定方法has_permission
            # 对象调用自己的绑定方法会把自己传入(权限类的对象,request,视图类的对象)
            if not permission.has_permission(request, self):
                self.permission_denied(
                    request,
                    message=getattr(permission, 'message', None),
                    code=getattr(permission, 'code', None)
                )
                
   -APIVIew的self.get_permissions(),273行上下
	return [permission() for permission in self.permission_classes]
   -self.permission_classes 就是咱们在视图类中配的权限类的列表
   -所以这个get_permissions返回的是 咱们在视图类中配的权限类的对象列表[UserTypePermession(),]


	# 总结:权限类源码
    	-为什么要写一个类,重写has_permission方法,有三个参数,为什么一定要return True或False,messgage可以做什么用

鸭子类型

单只在面向对象中,子类不需要显示继承某个类,只要类中有某个方法或属性,那我就属于这个类

# 假设有个鸭子类Duck类,有两个方法,run,speak方法
# 假设又有一个普通鸭子类,PDuck,如果它也是鸭子,它需要继承Duck类,只要继承了鸭子类,什么都不需要写,普通鸭子类的对象就是鸭子这种类型;如果不继承,普通鸭子类的对象就不是鸭子这种类型
#假设又有一个唐老鸭子类,TDuck,如果它也是鸭子,它需要继承Duck类,只要继承了鸭子类,什么都不需要写,唐老鸭子类的对象就是鸭子这种类型;如果不继承,唐老鸭子类的对象就不是鸭子这种类型

# python不推崇这个,它推崇鸭子类型,指的是
不需要显示的继承某个类,只要我的类中有run和speak方法,我就是鸭子这个类
# 有小问题:如果使用python鸭子类型的写法,如果方法写错了,它就不是这个类型了,会有问题
# python为了解决这个问题:
	-方式一:abc模块,装饰后,必须重写方法,不重写就报错
    -方式二:drf源码中使用的:父类中写这个方法,但没有具体实现,直接抛异常

注意

# django的配置文件不要乱导入,乱导入可能会出错
'''
django的运行是在加载完配置文件后才能运行
因为模块的导入会执行那个模块,而这个模块中又有别的导入,别的导入必须djagno运行起来才能使用
'''

作业1

1 编写图书和出版社的5个接口,所有接口都要有一分钟访问5次的频率限制

 图书的接口需要登录才能方法,出版社的接口需要登录,并且是超级用户才能访问

views.py

from rest_framework.response import Response
from .serializer import BookSerializer, PublishSerializer
from .models import Book, Publish, User, UserToken
from rest_framework.viewsets import ModelViewSet, ViewSet
import uuid
from rest_framework.decorators import action
from .throttling import UserThrottling

class BookView(ModelViewSet):
    throttle_classes = [UserThrottling] # 接口访问频率

    permission_classes = []
    # authentication_classes = []
    queryset = Book.objects.all()
    serializer_class = BookSerializer


class PublishView(ModelViewSet):
    throttle_classes = [UserThrottling] # 接口访问频率
    queryset = Publish.objects.all()
    serializer_class = PublishSerializer


class UserView(ViewSet):
    throttle_classes = [UserThrottling]  # 接口访问频率
    permission_classes = []
    authentication_classes = []
    @action(methods=['POST'], detail=False, url_path='login', url_name='login')
    def login(self, request):
        username = request.data.get('username')
        password = request.data.get('password')
        user = User.objects.filter(username=username, password=password).first()  # user拿到的就是当前登陆的用户
        if user:
            token = str(uuid.uuid4())
            UserToken.objects.update_or_create(defaults={'token': token}, user=user)
            return Response({'code': 100, 'msg': '真牛逼,竟然登上了','token':token})
        else:
            return Response({'code': 101, 'msg': '你真的是二臂啊,这都登不上'})

settings.py

REST_FRAMEWORK = {
    # 认证管理
    'DEFAULT_AUTHENTICATION_CLASSES': ['app01.auth.LoginAuth', ],
    # 权限管理
    'DEFAULT_PERMISSION_CLASSES': ['app01.permission.UserTypePermission'],
    # 频率管理
    'DEFAULT_THROTTLE_CLASSES':['app01.throttling.UserThrottling'],
    # 每分钟访问频率为五次
    'DEFAULT_THROTTLE_RATES': {
        'throttle': '5/m'
    }

}

models.py

from django.db import models


# Create your models here.


class Book(models.Model):
    name = models.CharField(max_length=32)
    price = models.CharField(max_length=32)
    publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)


class Publish(models.Model):
    name = models.CharField(max_length=32)
    address = models.CharField(max_length=32)


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


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

auth.py

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


class LoginAuth(BaseAuthentication):
    def authenticate(self, request):
        token = request.GET.get('token')
        user_token = UserToken.objects.filter(token=token).first()
        if user_token:
            return user_token.user, token
        else:
            raise AuthenticationFailed('你真过分哎,还没登陆就想看')

permission.py

from rest_framework.permissions import BasePermission


class UserTypePermission(BasePermission):
    def has_permission(self, request, view):
        if request.user.user_type == 1:
            return True
        else:
            return False

throttling.py

from rest_framework.throttling import SimpleRateThrottle

class UserThrottling(SimpleRateThrottle):
    scope = 'throttle'
    def get_cache_key(self, request, view):
        return request.META.get('REMOTE_ADDR')

serializer.py

from rest_framework import serializers
from .models import Book,Publish


class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = '__all__'

class PublishSerializer(serializers.ModelSerializer):
    class Meta:
        model = Publish
        fields = '__all__'

urls.py

from django.contrib import admin
from django.urls import path,include
from app01 import views
from rest_framework.routers import SimpleRouter
router = SimpleRouter()
router.register('user',views.UserView,'user')
router.register('books',views.BookView,'books')
router.register('publish',views.PublishView,'publish')

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

 

标签:self,request,认证,token,源码,user,import,三大
From: https://www.cnblogs.com/scx-xiaochun/p/16772656.html

相关文章