Drf从入门到精通六(DRF三大认证认证权限频率、自动生成路由、认证权限频率源码分析)

一、路由

1)自动生成路由

'''
	首先想自动生成路由就必须继承ViewSetmixin及其子类的视图类
'''
# 模拟一个自动生成路由练习

models.py		# 创建一个书模型
	class Book(models.Model):	
	    name = models.CharField(max_length=32, )
	    price = models.CharField(max_length=32, )
	    publish = models.CharField(max_length=32)

views.py		# 快速写好五个接口
	from rest_framework.viewsets import ViewSetMixin, ViewSet, GenericViewSet, ModelViewSet, ReadOnlyModelViewSet

	class BookView(ModelViewSet):	# 必须继承ViewSetmixin及其子类的视图类
	    queryset = Book.objects.all()
	    serializer_class = BookSerializer

serializer.py	# 序列化
	from .models import Book
	from rest_framework import serializers
	
	class BookSerializer(serializers.ModelSerializer):
	    class Meta:
	        model = Book
	        fields = '__all__'	# 所有字段

urls.py			# API接口
	urlpatterns = [
	    path('admin/', admin.site.urls),
	    path('books/', views.BookView.as_view({'get': 'list', 'post': 'create'})),
	    path('books/<int:pk>/', views.BookView.as_view({'get': 'retrieve', 'put': 'update', 'delete': 'destroy'})),
	]

'''
	正常来说这里我们的五个几口写完了 但是并没有实现自动生成API
	想要实现自动生成API需要使用到模块 rest_framework.routers里的SimpleRouter, DefaultRouter
'''

from rest_framework.routers import SimpleRouter, DefaultRouter	# 导入模块
router = SimpleRouter()		# 实例化路由
router.register('books', views.BookView, 'books')		!!!! 注意可以注册多个
# 注册 第一参数 路径	第二个参数 视图类	第三个参数 别名
把自动生成的路由添加到Urlpatterns中 总共有两种方法:
方式1:	# 直接添加 +=
	urlpatterns+=router.urls
方式2:	# 添加到urlpatterns里面使用到include
	[path('', include(router.urls))]

'''
	这个时候就能看到自动生成的路由 这个自动生成的路由就写好了
	
	admin/
	^books/$ [name='books-list']
	^books/(?P<pk>[^/.]+)/$ [name='books-detail']
	
	如果使用DefaultRouter 就会发生一个变化 多几个路由 主要的是多了一个跟路径 可以显示注册过的路由以及美化的页面
	
	admin/
	^books/$ [name='books-list']
	^books\.(?P<format>[a-z0-9]+)/?$ [name='books-list']
	^books/(?P<pk>[^/.]+)/$ [name='books-detail']
	^books/(?P<pk>[^/.]+)\.(?P<format>[a-z0-9]+)/?$ [name='books-detail']
	^$ [name='api-root']
	^\.(?P<format>[a-z0-9]+)/?$ [name='api-root']
'''

在这里插入图片描述

在这里插入图片描述

2)Action装饰器的使用

'''
	Action主要用于在视图函数中含有一些其他名称的方法 这个时候只有5个参数(create, update, delete...)
	我们不知道怎么去执行它 这个时候就必须要使用action装饰器做映射了
'''
views.py
	class UserView(ViewSet):
	    def login(self, request):
	        return Response('ok')
	        
urls.py
	from rest_framework.routers import SimpleRouter, DefaultRouter
	router = SimpleRouter()
	router.register('users', views.UserView, 'users')
	urlpatterns += router.urls
-------------------------------------------------------------------------
# 视图类中有其他名称的方法了需要使用Action装饰器了
views.py
	class UserView(ViewSet):
	    @action(methods=['GET', 'POST'], detail=False, url_path='login', url_name='login')
	    def login(self, request):
	        return Response('ok')

# 自动生产的路由
	^users/login/$ [name='users-login']

	'''
		method:   能够支持的请求方式 以列表的方式存入
		detail:   默认是False 控制生成的路由是什么样子 ^users/(?P<pk>[^/.]+)/login/$ [name='users-login']如果是True则会加上PK	  
		ulr_path: 控制生成的/user/后面的路径是什么 如果不写默认以方法名 
		url_name: 别名 用于反向解析
	'''

在这里插入图片描述

二、认证

1)编写登录接口

# 认证是基于登录的接口上面操作的 所以前戏编写一个简单的登录接口

models.py
	class User(models.Model):  # 简易的用户信息账号密码
    username = models.CharField(max_length=32)
    password = models.CharField(max_length=32)

    def __str__(self):
        return self.username


	class UserToken(models.Model):  # 用户信息登录记录表
	    user = models.OneToOneField(to='User', on_delete=models.CASCADE)  # 一对一关联
	    token = models.CharField(max_length=32, null=True)  # 如果用户没有登录则没有值 如果登录则有值

views.py
	class UserView(ViewSet):
    @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()    # 比对用户名与密码
        if user:
            token = str(uuid.uuid4())  # uuid4 随机获得永不重复的字符串 机制跟Cookie中的验证码一样
            UserToken.objects.update_or_create(defaults={'token': token}, user=user)   
            # 通过user去UserToken表中查数据,如果能查到,使用defaults的数据更新,如果查不到,直接通过user和defaults的数据新增
            return Response({'code': 100, 'msg': '登录成功', 'token': token})
        else:
            return Response({'code': 101, 'msg': '用户名或密码错误'})

urls.py
	from rest_framework.routers import SimpleRouter, DefaultRouter
	router = SimpleRouter()
	router.register('users', views.UserView, 'users')
	urlpatterns += router.urls

'''这个时候一个简单的登录接口就写好了 每次登录都会更新Token 相当于登录了之前的设备就无效了 '''

在这里插入图片描述

2)认证类

'''
	现在我们又了登录接口 但是我们想其他的接口必须登录之后才能访问呢?
	思路:
		 通过认证类完成,使用步骤
			1 写一个认证类,继承BaseAuthentication
		    2 重写authenticate方法,在内部做认证
		    3 如果认证通过,返回2个值
		    4 认证不通过抛AuthenticationFailed异常
		    5 只要返回了两个值,在后续的request.user 就是当前登录用户
'''


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


class LoginAuth(BaseAuthentication):
    def authenticate(self, request):    # 重写authenticate方法 做内部认证
        token = request.GET.get('token')    # 获取Token
        user_token = UserToken.objects.filter(token=token).first()  # 对比token
        if user_token:
            return user_token.user, token   # 如果通过认证返回两个值 不通过抛异常
        else:                   # 只要返回了两个值,在后续的request.user 就是当前登录用户
            raise AuthenticationFailed('您还没有登录哦')


'''
	如果想让某个视图类登录后才能访问
		方式1:	局部认证
		
				from .auth import LoginAuth
				
				class BookView(ModelViewSet):
				    authentication_classes = [LoginAuth,]
				    queryset = Book.objects.all()
				    serializer_class = BookSerializer
				    
		方式2:	全局认证
			REST_FRAMEWORK={
            'DEFAULT_AUTHENTICATION_CLASSES':['app01.auth.LoginAuth',]
        	}
		全局认证但是又不想某一个不认证呢?
		在视图类里面写入authentication_classes = []清空即可!!!

'''

在这里插入图片描述

三、权限

'''
	权限就是相当于我们的管理员 没有权限就是普通用户 
	类似于python中的admin管理员我们只需要给用户加上字段admin 1,超级管理员 2,普通用户
	我们现在目的 出版社所有的接口必须登录而且只有管理员才能够访问(延着上放模型继续写)
'''

models.py
	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, '3b用户')))

'''
	权限类其实跟认证类一样只需要继承类修改方法
	思路:
		-第一步:写一个类,继承BasePermission
	    -第二步:重写has_permission方法
	    -第三步:在方法中校验用户是否有权限(request.user就是当前登录用户)
	    -第四步:如果有权限,返回True,没有权限,返回False
	    -第五步:self.message 是给前端的提示信息
'''

permission.py	# 创建的类文件
	from rest_framework.permissions import BasePermission

	class UserTypePermission(BasePermission):       # 随意一个类继承BasePermission
	    def has_permission(self, request, view):    # 重写has_permission方法
	        if request.user.user_type == 1:         # 获取用户的管理员信息 如果有权限返回True 没权限False
	            return True
	        else:                                   # 报错信息渲染
	            self.message = '您是%s, 您没有权限访问哦!' % request.user.get_user_type_display() 
	            return False

'''
	使用方法:
		1.全局使用
			REST_FRAMEWORK = {
			    'DEFAULT_PERMISSION_CLASSES': ['app01.permission.UserTypePermission']
			}
			
		2.局部使用
			class PublishView(ListCreateAPIView):
			   permission_classes = [UserTypePermission, ]
			   
		3.局部禁用
			class PublishView(ListCreateAPIView):
   				permission_classes = [ ]
'''

在这里插入图片描述

四、频率

'''
	频率就涉及到爬虫方面的 为了防止网站被攻击一次性发了10多万次访问 这个时候我们就可以做一个频率限制 一分钟让访问几次
	思路:
		-第一步:写一个类:继承SimpleRateThrottle
	    -第二步:重写get_cache_key,返回唯一的字符串,会以这个字符串做频率限制
	    -第三步:写一个类属性scop='随意写',必须要跟配置文件对象
	    -第四步:配置文件中写
	      'DEFAULT_THROTTLE_RATES': {
	        '随意写': '3/m'  # 3/h  3/s  3/d
	    	}
'''

thorttling.py 	# 自定频率类文件
	from rest_framework.throttling import BaseThrottle, SimpleRateThrottle
	
	class MyThrottling(SimpleRateThrottle):     # 继承SimpleRateThrottle
	    scope = 'luffy'     # 类属性 属性名称可以随意取 但是要跟配置文件对应
	
	    def get_cache_key(self, request, view):
	        return request.META.get('REMOTE_ADDR')
	
	    '''
	        返回什么 频率就可以做什么限制
	        通过用户IP地址进行限制 REMOTE_ADDR
	    '''

settings.py
	REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_RATES': {
        'luffy': '5/m'  # m分 h时 s秒 d天次数
    },
}

'''
	使用方法:
		1.全局使用
			REST_FRAMEWORK = {
			    ''DEFAULT_THROTTLE_CLASSES': ['app01.throttling.MyThrottling'],
			    }
			
		2.局部使用
			class BookView(ModelViewSet):
    			throttle_classes = [MyThrottling, ]
			   
		3.局部禁用
			class BookView(ModelViewSet):
    			throttle_classes = [ ]
'''

在这里插入图片描述

五、认证源码分析

# 写个认证类,重写某个方法,配置在视图类上,就有认证了---》认证类加了,在视图类的方法中,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类中找:220def user(self):
            if not hasattr(self, '_user'): # Request类的对象中反射_user
                with wrap_attributeerrors():
                    self._authenticate()  # 第一次会走这个代码
            return self._user
    -Request的self._authenticate()---373def _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)异常

六、权限源码分析

-先读最简单的权限执行流程---》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 TrueFalse,messgage可以做什么用

七、简单分析频率源码

# 源码分析:
	-之前咱们读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的check_throttles:351上下
        def check_throttles(self, request):
            throttle_durations = []
            for throttle in self.get_throttles():
                if not throttle.allow_request(request, self):
                    throttle_durations.append(throttle.wait())
                    
                    
   -总结:要写频率类,必须重写allow_request方法,返回True(没有到频率的限制)或False(到了频率的限制)

技术小白记录学习过程,有错误或不解的地方请指出,如果这篇文章对你有所帮助请点点赞收藏+关注谢谢支持 !!!

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LoisMay

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值