drf 认证校验及源码分析
认证校验
认证校验是十分重要的,如用户如果不登陆就不能访问某些接口。
再比如用户不登陆就不能够对一个接口做哪些操作。
drf中认证的写法流程如下:
1.写一个类,继承BaseAuthentication,并且覆写其authenticate方法
2.当认证通过后应该返回两个值,并且第一个值会传递给request.user这个属性中,第二个值将会传递给request.auth这个属性中
3.如果认证失败,则抛出异常APIException或者AuthenticationFailed,它会自动捕获并返回
4.当前认证类设置是全局使用还是局部使用
准备工作
我们有一个登录功能,并且还有一个查询商品的接口,只有当用户登录后才能进行查询,否则就不可以。
模型表
两张表如下:
from django.db import models
class User(models.Model):
# 用户
user_id = models.AutoField(primary_key=True)
user_name = models.CharField(max_length=32)
user_password = models.CharField(max_length=32)
user_token = models.CharField(max_length=64,unique=True,null=True) # token,唯一
def __str__(self):
return self.user_name
class Meta:
db_table = ""
managed = True
verbose_name = "User"
verbose_name_plural = "Users"
class Merchandise(models.Model):
# 商品
merchandise_id = models.AutoField(primary_key=True)
merchandise_name = models.CharField(max_length=32)
merchandise_price = models.IntegerField()
def __str__(self):
return self.merchandise_name
class Meta:
db_table = ""
managed = True
verbose_name = "Merchandise"
verbose_name_plural = "Merchandises"
用户表的数据如下:

商品表的数据如下:

现在,只有当用户登录后,才能够访问商品的接口。
也就是说,用户的token自动如果为空,将会被认为没有登陆。
序列类
下面是序列类,我们只展示商品,用户列表将不会展示:
from rest_framework.serializers import ModelSerializer
from . import models
class MerchandiseModelSerializer(ModelSerializer):
class Meta:
model = models.Merchandise
fields = "__all__"
视图
视图,我们只写了关于用户登录与商品的接口:
from uuid import uuid4
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet
from . import models
from . import ser
from . import authLogin # 导入认证的文件
class MerchandiseAPI(ModelViewSet):
queryset = models.Merchandise.objects.all()
serializer_class = ser.MerchandiseModelSerializer
class Login(APIView):
def post(self,request):
# 代表用户登录
login_msg = {
"user_name": request.data.get("user_name"),
"user_password": request.data.get("user_password"),
}
user_obj = models.User.objects.filter(**login_msg).first()
if user_obj:
token = uuid4() # 生成随机字符串
user_obj.user_token = token
user_obj.save()
return Response(data="登录成功",headers={"token":token}) # 返回随机字符串
else:
return Response(data="登录失败,用户名或密码错误")
url
使用自动生成路由:
from django.contrib import admin
from django.urls import path, re_path
from rest_framework.routers import SimpleRouter
from app01 import views
router = SimpleRouter()
router.register("merchandises",views.MerchandiseAPI)
urlpatterns = [
path('admin/', admin.site.urls),
path('login/',views.Login.as_view()),
]
urlpatterns.extend(router.urls)
基本使用
认证类
接下来我们要书写一个认证类:
from rest_framework.authentication import BaseAuthentication # 继承的基类
from rest_framework.exceptions import AuthenticationFailed # 异常
from . import models
from django.http import request
class LoginVerify(BaseAuthentication):
def authenticate(self, request):
token = request.META.get("HTTP_TOKEN")
# 如果在请求头中设置的是token的key名,获取时一定要全大写并加上HTTP
if not token:
raise AuthenticationFailed("请求失败,请求头中缺少token")
else:
user_obj = models.User.objects.filter(user_token=token).first() # 获取用户对象
if user_obj:
return user_obj,user_obj.user_token # 返回用户本身和token。这样request.user里面就能拿到该用户了
else:
raise AuthenticationFailed("token不存在,用户不存在,请不要伪造登录")
局部使用
只需要在商品接口中设置一个类属性,该接口便会进行认证。
class MerchandiseAPI(ModelViewSet):
authentication_classes = [authLogin.LoginVerify] # 使用认证
queryset = models.Merchandise.objects.all()
serializer_class = ser.MerchandiseModelSerializer
全局使用
只需要在settings.py中进行配置。
REST_FRAMEWORK={
"DEFAULT_AUTHENTICATION_CLASSES":["app01.authLogin.LoginVerify",]
}
如果想取消某个接口的认证,则在其中设置类属性authentication_classes是一个空列表。
如下所示,登录功能不需要验证,我们对他取消掉即可。
class Login(APIView):
authentication_classes = []
源码分析
流程分析
由于modelViewSet继承自APIView,所以我们直接看as_view(),在下面这一句代码中,将会对request进行二次封装。
def dispatch(self, request, *args, **kwargs):
self.args = args
self.kwargs = kwargs
request = self.initialize_request(request, *args, **kwargs) # 这里
self.request = request
self.headers = self.default_response_headers # deprecate?
在二次封装中,实例化出了一个Request对象并返回了,在实例化时,会调用self.get_authenticators()方法,此时的self是我们自定义的视图类,切记这一点。
def initialize_request(self, request, *args, **kwargs):
"""
Returns the initial request object.
"""
parser_context = self.get_parser_context(request)
return Request(
request,
parsers=self.get_parsers(),
authenticators=self.get_authenticators(), # 看这里,获取认方式
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)
下面是get_authenticators()的代码,可以看见它会循环self.authentication_classes这个可迭代对象,如果你没有传递这个可迭代对象,那么该对象是一个默认的设置。
def get_authenticators(self):
return [auth() for auth in self.authentication_classes] # ( authLogin.LoginVerify调用,实例化 )
如果没有传递,将会找到APIView中的默认设置:
class APIView(View):
renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
parser_classes = api_settings.DEFAULT_PARSER_CLASSES
authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES # 默认的设置,默认的认证类,可以自己看一下
如果有进行传递,可以发现它是使用了一个括号,这就代表会调用,由于传入的是一个类,所以它会进行实例化。
所以我们可以认为request.authenticators这个参数是一个tuple,里面包含了认证类的实例化对象。
然后,request就被二次包装完毕了。接下来执行 self.initial(),现在的self依然是我们自定义的视图类。
def dispatch(self, request, *args, **kwargs):
"""
`.dispatch()` is pretty much the same as Django's regular dispatch,
but with extra hooks for startup, finalize, and exception handling.
"""
self.args = args
self.kwargs = kwargs
request = self.initialize_request(request, *args, **kwargs)
self.request = request
self.headers = self.default_response_headers # deprecate?
try:
self.initial(request, *args, **kwargs)
下面是self.inital()的代码,
def initial(self, request, *args, **kwargs):
self.format_kwarg = self.get_format_suffix(**kwargs)
self.perform_authentication(request) # 只看这个,认证相关的
self.check_permissions(request)
self.check_throttles(request)
到了self.perform_authentication()时,它传递进了个request,并且会去找user这个属性抑或是被property装饰的方法,所以我们需要到Request这个类中去找,需要注意的是如果user是一个方法,这代表会自动传递进self,此时的self则是我们经过二次封装的request对象。
可以发现它是一个被装饰的方法。很显然我们没有_user这个方法或属性,会执行with语句,其实直接看self._authenticate()即可。再次强调,此次的self是二次封装的request对象。
@property
def user(self):
if not hasattr(self, '_user'):
with wrap_attributeerrors():
self._authenticate()
return self._user
下面是整个代码的核心。
def _authenticate(self):
for authenticator in self.authenticators: # 循环认证类对象 ( authLogin.LoginVerify的实例化 )
try:
user_auth_tuple = authenticator.authenticate(self) # 这里会找authenticate方法并将request对象进行传递,我们的认证类继承了BaseAuthentication这个类,它会实现一个接口方法, 但会抛出异常。
except exceptions.APIException: # 如果没有实现接口方法,或在验证时抛出异常都会被这里捕获
self._not_authenticated() # 执行这里 self.user将会是匿名用户AnonymousUser,而self.auth则是None
raise
if user_auth_tuple is not None: # 如果返回的值不是空
self._authenticator = authenticator
self.user, self.auth = user_auth_tuple # 分别赋值给self.user,以及self.auth中
return # 返回
self._not_authenticated() # 上面有认证对象就会return,没有还是设置匿名用户和None
最后总结
其实看了源码后,你可以发现我们的认证类可以不继承BaseAuthentication,但是推荐继承会更规范,因为这个基类实现了抽象接口。
其次,它将返回的两个值分别赋值给了request.user以及request.auth。
如果你没有返回值,那么对应的,request.user就是匿名用户,request.auth就是None。
如果你没有配置认证类,其实它会走默认的认证类。
老规矩,关于配置认证类时依旧是先用局部的,再用全局的,最后是用默认的,如果你的上面的源码确实有感觉了的话,应该能够看懂。
drf 认证校验及源码分析的更多相关文章
- DRF框架(一)——restful接口规范、基于规范下使用原生django接口查询和增加、原生Django CBV请求生命周期源码分析、drf请求生命周期源码分析、请求模块request、渲染模块render
DRF框架 全称:django-rest framework 知识点 1.接口:什么是接口.restful接口规范 2.CBV生命周期源码 - 基于restful规范下的CBV接口 3.请求组件 ...
- 探索drf执行流程之APIView源码分析
Django REST framework 简介 现在新一代web应用都开始采用前后端分离的方式来进行,淘汰了以前的服务器端渲染的方式.而实现前后端分离是通过Django REST framework ...
- drf 简介以及部分源码分析
目录 复习 drf框架 全称:django-rest framework 知识点 接口 restful接口规范 基于restful规范的原生Django接口 主路由:url.py api组件的子路由: ...
- drf 视图使用及源码分析
前言 drf视图的源码非常的绕,但是实现的功能却非常的神奇. 它能够帮你快速的解决ORM增删改查的重复代码,非常的方便好用. 下面是它源码中的一句话: class ViewSetMixin: &quo ...
- DRF中的APIView源码分析
首先写一个简单的drf接口 from rest_framework.views import APIView from rest_framework.response import Response ...
- sentinel流控规则校验之源码分析
前言: 上节给大家把sentinel流控整个执行大致过了,但涉及到最核心的流控算法还没有讲,先提前说明一下 sentinel用的流控算法是令牌桶算法,参考了Guava的RateLimiter,有读过R ...
- Django之REST framework源码分析
前言: Django REST framework,是1个基于Django搭建 REST风格API的框架: 1.什么是API呢? API就是访问即可获取数据的url地址,下面是一个最简单的 Djang ...
- django 之(二) --- 源码分析
CBV类视图继承 CBV:继承自View:注册的时候使用的as_view() 入口 不能使用请求方法的名字作为参数的名字 只能接受已经存在的属性对应的参数 定义了一个view 创建了一个类视图对象 保 ...
- Django drf:序列化增删改查、局部与全局钩子源码流程、认证源码分析、执行流程
一.序列化类的增.删.改.查 用drf的序列化组件 -定义一个类继承class BookSerializer(serializers.Serializer): -写字段,如果不指定source ...
随机推荐
- 报错: Failed to establish a new connection: [WinError 10061] 由于目标计算机积极拒绝,无法连接。'))
你没打开 1.双击打开 2.点击:
- 【MindSpore】Docker上成功使用MindSpore1.0.0的GPU版本
本文是在宿主机Ubuntu16.04上安装Docker(nvidia-docker),并成功进行MindSpore1.0.0的GPU训练: Ubuntu 16.04 Docker Nvidia-doc ...
- Git源代码管理笔记
很早之前也学过git但是没有详细的记录,这次是做个复习,巩固 若有不对的地方请您指出 目录 Git和SVN有什么区别? 什么是Git? github仓库 Star和Fork Pull request ...
- Linux里面的压缩和解压类指令
gzip/gunzip 指令 ( .gz 不能压缩目录) gzip 用于压缩文件, gunzip 用于解压的 . gzip gzip命令用来压缩文件.gzip是个使用广泛的压缩程序,文件经它压缩过后 ...
- 微服务实战系列(十)-网关高可用之中间件Keepalived
1.场景描述 因为要做网关的高可用,用到了keepalived+nginx,来保证nginx的高可用,如下图: 安装了keepavlived,走了一些弯路,记录下吧,nginx的安装就不多说了,博客已 ...
- Python实现的数据结构与算法之基本搜索详解
一.顺序搜索 顺序搜索 是最简单直观的搜索方法:从列表开头到末尾,逐个比较待搜索项与列表中的项,直到找到目标项(搜索成功)或者 超出搜索范围 (搜索失败). 根据列表中的项是否按顺序排列,可以将列表分 ...
- Docker 开启非认证的2375端口,提供外部访问 Docker
1.编辑 Docker 服务的配置文件 vi /usr/lib/systemd/system/docker.service 或者 vi /lib/systemd/system/docker.servi ...
- vue获取下拉框select的值
1.我写的是循环遍历,然后获取id :value="v.id"这就是获取的id然后打印就可以获取id了
- OAth 2.0 的白话讲解
一.OAuth2.0是什么,主要做什么用的? 官方注解 简单说,OAuth 就是一种授权机制.数据的所有者告诉系统,同意授权第三方应用进入系统,获取这些数据.系统从而产生一个短期的进入令牌(token ...
- CSS的元素显示模式与转换
CSS的元素显示模式与转换 1. CSS的元素显示模式 1.1 块元素 <div>标签是最典型的块元素.另外常见的块元素有h1~h6.p.ul.ol.li等. 特点: 独占一行 高度.宽度 ...