认证校验

   认证校验是十分重要的,如用户如果不登陆就不能访问某些接口。

   再比如用户不登陆就不能够对一个接口做哪些操作。

   drf中认证的写法流程如下:

   1.写一个类,继承BaseAuthentication,并且覆写其authenticate方法

   2.当认证通过后应该返回两个值,并且第一个值会传递给request.user这个属性中,第二个值将会传递给request.auth这个属性中

   3.如果认证失败,则抛出异常APIException或者AuthenticationFailed,它会自动捕获并返回

   4.当前认证类设置是全局使用还是局部使用

准备工作

   我们有一个登录功能,并且还有一个查询商品的接口,只有当用户登录后才能进行查询,否则就不可以。

模型表

   两张表如下:

  1. from django.db import models
  2. class User(models.Model):
  3. # 用户
  4. user_id = models.AutoField(primary_key=True)
  5. user_name = models.CharField(max_length=32)
  6. user_password = models.CharField(max_length=32)
  7. user_token = models.CharField(max_length=64,unique=True,null=True) # token,唯一
  8. def __str__(self):
  9. return self.user_name
  10. class Meta:
  11. db_table = ""
  12. managed = True
  13. verbose_name = "User"
  14. verbose_name_plural = "Users"
  15. class Merchandise(models.Model):
  16. # 商品
  17. merchandise_id = models.AutoField(primary_key=True)
  18. merchandise_name = models.CharField(max_length=32)
  19. merchandise_price = models.IntegerField()
  20. def __str__(self):
  21. return self.merchandise_name
  22. class Meta:
  23. db_table = ""
  24. managed = True
  25. verbose_name = "Merchandise"
  26. verbose_name_plural = "Merchandises"

   用户表的数据如下:

  

   商品表的数据如下:

  

   现在,只有当用户登录后,才能够访问商品的接口。

   也就是说,用户的token自动如果为空,将会被认为没有登陆。

序列类

   下面是序列类,我们只展示商品,用户列表将不会展示:

  1. from rest_framework.serializers import ModelSerializer
  2. from . import models
  3. class MerchandiseModelSerializer(ModelSerializer):
  4. class Meta:
  5. model = models.Merchandise
  6. fields = "__all__"

视图

   视图,我们只写了关于用户登录与商品的接口:

  1. from uuid import uuid4
  2. from rest_framework.views import APIView
  3. from rest_framework.response import Response
  4. from rest_framework.viewsets import ModelViewSet
  5. from . import models
  6. from . import ser
  7. from . import authLogin # 导入认证的文件
  8. class MerchandiseAPI(ModelViewSet):
  9. queryset = models.Merchandise.objects.all()
  10. serializer_class = ser.MerchandiseModelSerializer
  11. class Login(APIView):
  12. def post(self,request):
  13. # 代表用户登录
  14. login_msg = {
  15. "user_name": request.data.get("user_name"),
  16. "user_password": request.data.get("user_password"),
  17. }
  18. user_obj = models.User.objects.filter(**login_msg).first()
  19. if user_obj:
  20. token = uuid4() # 生成随机字符串
  21. user_obj.user_token = token
  22. user_obj.save()
  23. return Response(data="登录成功",headers={"token":token}) # 返回随机字符串
  24. else:
  25. return Response(data="登录失败,用户名或密码错误")

url

   使用自动生成路由:

  1. from django.contrib import admin
  2. from django.urls import path, re_path
  3. from rest_framework.routers import SimpleRouter
  4. from app01 import views
  5. router = SimpleRouter()
  6. router.register("merchandises",views.MerchandiseAPI)
  7. urlpatterns = [
  8. path('admin/', admin.site.urls),
  9. path('login/',views.Login.as_view()),
  10. ]
  11. urlpatterns.extend(router.urls)

基本使用

认证类

   接下来我们要书写一个认证类:

  1. from rest_framework.authentication import BaseAuthentication # 继承的基类
  2. from rest_framework.exceptions import AuthenticationFailed # 异常
  3. from . import models
  4. from django.http import request
  5. class LoginVerify(BaseAuthentication):
  6. def authenticate(self, request):
  7. token = request.META.get("HTTP_TOKEN")
  8. # 如果在请求头中设置的是token的key名,获取时一定要全大写并加上HTTP
  9. if not token:
  10. raise AuthenticationFailed("请求失败,请求头中缺少token")
  11. else:
  12. user_obj = models.User.objects.filter(user_token=token).first() # 获取用户对象
  13. if user_obj:
  14. return user_obj,user_obj.user_token # 返回用户本身和token。这样request.user里面就能拿到该用户了
  15. else:
  16. raise AuthenticationFailed("token不存在,用户不存在,请不要伪造登录")

局部使用

   只需要在商品接口中设置一个类属性,该接口便会进行认证。

  1. class MerchandiseAPI(ModelViewSet):
  2. authentication_classes = [authLogin.LoginVerify] # 使用认证
  3. queryset = models.Merchandise.objects.all()
  4. serializer_class = ser.MerchandiseModelSerializer

全局使用

   只需要在settings.py中进行配置。

  1. REST_FRAMEWORK={
  2. "DEFAULT_AUTHENTICATION_CLASSES":["app01.authLogin.LoginVerify",]
  3. }

   如果想取消某个接口的认证,则在其中设置类属性authentication_classes是一个空列表。

   如下所示,登录功能不需要验证,我们对他取消掉即可。

  1. class Login(APIView):
  2. authentication_classes = []

源码分析

流程分析

   由于modelViewSet继承自APIView,所以我们直接看as_view(),在下面这一句代码中,将会对request进行二次封装。

  1. def dispatch(self, request, *args, **kwargs):
  2. self.args = args
  3. self.kwargs = kwargs
  4. request = self.initialize_request(request, *args, **kwargs) # 这里
  5. self.request = request
  6. self.headers = self.default_response_headers # deprecate?

   在二次封装中,实例化出了一个Request对象并返回了,在实例化时,会调用self.get_authenticators()方法,此时的self是我们自定义的视图类,切记这一点。

  1. def initialize_request(self, request, *args, **kwargs):
  2. """
  3. Returns the initial request object.
  4. """
  5. parser_context = self.get_parser_context(request)
  6. return Request(
  7. request,
  8. parsers=self.get_parsers(),
  9. authenticators=self.get_authenticators(), # 看这里,获取认方式
  10. negotiator=self.get_content_negotiator(),
  11. parser_context=parser_context
  12. )

   下面是get_authenticators()的代码,可以看见它会循环self.authentication_classes这个可迭代对象,如果你没有传递这个可迭代对象,那么该对象是一个默认的设置。

  1. def get_authenticators(self):
  2. return [auth() for auth in self.authentication_classes] # ( authLogin.LoginVerify调用,实例化 )

   如果没有传递,将会找到APIView中的默认设置:

  1. class APIView(View):
  2. renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
  3. parser_classes = api_settings.DEFAULT_PARSER_CLASSES
  4. authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES # 默认的设置,默认的认证类,可以自己看一下

   如果有进行传递,可以发现它是使用了一个括号,这就代表会调用,由于传入的是一个类,所以它会进行实例化。

   所以我们可以认为request.authenticators这个参数是一个tuple,里面包含了认证类的实例化对象。

   然后,request就被二次包装完毕了。接下来执行 self.initial(),现在的self依然是我们自定义的视图类。

  1. def dispatch(self, request, *args, **kwargs):
  2. """
  3. `.dispatch()` is pretty much the same as Django's regular dispatch,
  4. but with extra hooks for startup, finalize, and exception handling.
  5. """
  6. self.args = args
  7. self.kwargs = kwargs
  8. request = self.initialize_request(request, *args, **kwargs)
  9. self.request = request
  10. self.headers = self.default_response_headers # deprecate?
  11. try:
  12. self.initial(request, *args, **kwargs)

   下面是self.inital()的代码,

  1. def initial(self, request, *args, **kwargs):
  2. self.format_kwarg = self.get_format_suffix(**kwargs)
  3. self.perform_authentication(request) # 只看这个,认证相关的
  4. self.check_permissions(request)
  5. self.check_throttles(request)

   到了self.perform_authentication()时,它传递进了个request,并且会去找user这个属性抑或是被property装饰的方法,所以我们需要到Request这个类中去找,需要注意的是如果user是一个方法,这代表会自动传递进self,此时的self则是我们经过二次封装的request对象。

   可以发现它是一个被装饰的方法。很显然我们没有_user这个方法或属性,会执行with语句,其实直接看self._authenticate()即可。再次强调,此次的self是二次封装的request对象。

  1. @property
  2. def user(self):
  3. if not hasattr(self, '_user'):
  4. with wrap_attributeerrors():
  5. self._authenticate()
  6. return self._user

   下面是整个代码的核心。

  1. def _authenticate(self):
  2. for authenticator in self.authenticators: # 循环认证类对象 ( authLogin.LoginVerify的实例化 )
  3. try:
  4. user_auth_tuple = authenticator.authenticate(self) # 这里会找authenticate方法并将request对象进行传递,我们的认证类继承了BaseAuthentication这个类,它会实现一个接口方法, 但会抛出异常。
  5. except exceptions.APIException: # 如果没有实现接口方法,或在验证时抛出异常都会被这里捕获
  6. self._not_authenticated() # 执行这里 self.user将会是匿名用户AnonymousUser,而self.auth则是None
  7. raise
  8. if user_auth_tuple is not None: # 如果返回的值不是空
  9. self._authenticator = authenticator
  10. self.user, self.auth = user_auth_tuple # 分别赋值给self.user,以及self.auth中
  11. return # 返回
  12. self._not_authenticated() # 上面有认证对象就会return,没有还是设置匿名用户和None

最后总结

   其实看了源码后,你可以发现我们的认证类可以不继承BaseAuthentication,但是推荐继承会更规范,因为这个基类实现了抽象接口。

   其次,它将返回的两个值分别赋值给了request.user以及request.auth

   如果你没有返回值,那么对应的,request.user就是匿名用户,request.auth就是None

   如果你没有配置认证类,其实它会走默认的认证类。

   老规矩,关于配置认证类时依旧是先用局部的,再用全局的,最后是用默认的,如果你的上面的源码确实有感觉了的话,应该能够看懂。

drf 认证校验及源码分析的更多相关文章

  1. DRF框架(一)——restful接口规范、基于规范下使用原生django接口查询和增加、原生Django CBV请求生命周期源码分析、drf请求生命周期源码分析、请求模块request、渲染模块render

    DRF框架    全称:django-rest framework 知识点 1.接口:什么是接口.restful接口规范 2.CBV生命周期源码 - 基于restful规范下的CBV接口 3.请求组件 ...

  2. 探索drf执行流程之APIView源码分析

    Django REST framework 简介 现在新一代web应用都开始采用前后端分离的方式来进行,淘汰了以前的服务器端渲染的方式.而实现前后端分离是通过Django REST framework ...

  3. drf 简介以及部分源码分析

    目录 复习 drf框架 全称:django-rest framework 知识点 接口 restful接口规范 基于restful规范的原生Django接口 主路由:url.py api组件的子路由: ...

  4. drf 视图使用及源码分析

    前言 drf视图的源码非常的绕,但是实现的功能却非常的神奇. 它能够帮你快速的解决ORM增删改查的重复代码,非常的方便好用. 下面是它源码中的一句话: class ViewSetMixin: &quo ...

  5. DRF中的APIView源码分析

    首先写一个简单的drf接口 from rest_framework.views import APIView from rest_framework.response import Response ...

  6. sentinel流控规则校验之源码分析

    前言: 上节给大家把sentinel流控整个执行大致过了,但涉及到最核心的流控算法还没有讲,先提前说明一下 sentinel用的流控算法是令牌桶算法,参考了Guava的RateLimiter,有读过R ...

  7. Django之REST framework源码分析

    前言: Django REST framework,是1个基于Django搭建 REST风格API的框架: 1.什么是API呢? API就是访问即可获取数据的url地址,下面是一个最简单的 Djang ...

  8. django 之(二) --- 源码分析

    CBV类视图继承 CBV:继承自View:注册的时候使用的as_view() 入口 不能使用请求方法的名字作为参数的名字 只能接受已经存在的属性对应的参数 定义了一个view 创建了一个类视图对象 保 ...

  9. Django drf:序列化增删改查、局部与全局钩子源码流程、认证源码分析、执行流程

    一.序列化类的增.删.改.查 用drf的序列化组件   -定义一个类继承class BookSerializer(serializers.Serializer):   -写字段,如果不指定source ...

随机推荐

  1. python学习笔记1之-python简介及其环境安装

    python学习笔记之-python简介及其环境安装 最近几年python之火不用多说,最近开始利用时间自学python,在学习的过程中,按照自己的思路和理解记录下学习的过程,并分享出来,如果正好你也 ...

  2. Spring学习(二)--Spring的IOC

    1.依赖反转模式 依赖反转:高层次的模块不应该依赖于低层次的模块,两者都应该依赖于抽象接口.抽象接口不应该依赖于具体实现.而具体实现则应该依赖于抽象接口. 在面向对象编程领域中,依赖反转原则(Depe ...

  3. django 3.1 序列化讲述

    序列化Django对象¶ Django的序列化框架提供了一种将Django模型"翻译"为其他格式的机制.通常,这些其他格式将基于文本,并用于通过电线发送Django数据,但是序列化 ...

  4. Tomcat 8.5集群配置

    示例 <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster" channelSendOptions= ...

  5. 日志分析平台ELK之日志收集器filebeat

    前面我们了解了elk集群中的logstash的用法,使用logstash处理日志挺好的,但是有一个缺陷,就是太慢了:当然logstash慢的原因是它依赖jruby虚拟机,jruby虚拟机就是用java ...

  6. 探究"补阶乘大法的本质"——糖水不等式!

    废话不多说先来康一条例题: 证明: 下面给出题目的一种解法(我称之为"补阶乘大法"): 思考:为什么补上一个阶乘(准确说不是阶乘,是两个数阶乘的之商)项,放缩后再给去掉,就能达到我 ...

  7. Lyndon Word相关

    Lyndon Word 定义 对于字符串 \(S\),若 \(S\) 的最小后缀为其本身,那么称 \(S\) 为 \(\text{Lyndon}\) 串(\(\text{Lyndon Word}\)) ...

  8. LPCTSTR的含义

    LPCTSTR: LP代表指针.C代表不可改变.T代表根据是否定义UNICODE宏而分别define为char或wchar_t.STR代表字符串. 例如: LPCTSTR lp="BMP F ...

  9. abp(net core)+easyui+efcore实现仓储管理系统——出库管理之二(五十)

    abp(net core)+easyui+efcore实现仓储管理系统目录 abp(net core)+easyui+efcore实现仓储管理系统--ABP总体介绍(一) abp(net core)+ ...

  10. shell-脚本开发基本规范及习惯

    1.shell-脚本开发基本规范及习惯 1.开头指定脚本解析器 #!/bin/sh 或#!/bin/bash 2.开头加版本版权等信息 #Date: 2018/3/26 #Author: zhangs ...