Django rest framework 源码分析 (1)----认证

时间:2022-09-07 19:55:17

一、基础

django 2.0官方文档

https://docs.djangoproject.com/en/2.0/

安装

pip3 install djangorestframework

假如我们想实现用户必须是登陆后才能访问的需求,利用restframework该如何的去实现,具体的源码流程又是怎么样的呢

为了有一个清晰的认识,先直接上代码,有一个清晰的认识,在剖析源码流程

首先先创建一个应用

python manage.py startapp app01

在应用 app01.views.py 下 的视图函数的代码如下

from rest_framework.views import APIViewfrom rest_framework import exceptions
from rest_framework.request import Request class MyAuthentication(object):
def authenticate(self,request):
token = request._request.GET.get('token')
# 获取用户名和密码,去数据校验
if not token:
raise exceptions.AuthenticationFailed('用户认证失败')
return ("dog",None) def authenticate_header(self,val):
pass class DogView(APIView):
authentication_classes = [MyAuthentication,] def get(self,request,*args,**kwargs):
print(request)
print(request.user)
self.dispatch
ret = {
'code':1000,
'msg':'xxx'
}
return HttpResponse(json.dumps(ret),status=201)

在项目的根目录下的urls.py 中添加路由的代码如下:

from app01 import views

urlpatterns = [
url(r'^dog/', views.DogView.as_view()),
]

如果想实现必须是登陆后才能进行请求的话,只需重写父类中的  authenticate  (进行一些逻辑认证)和 authenticate_header   就能达到认证的效果

启动项目,在浏览器中输入

http://127.0.0.1:8000/dog/?token=123

‘携带token返回的结果如下

{"code": 1000, "msg": "xxx"}

  

没有携带token返回的结果如下

http://127.0.0.1:8000/dog

  

Django rest framework 源码分析 (1)----认证

源码分析

那么上面的源码内部又是如何是现代的呢

源码流程图

Django rest framework 源码分析 (1)----认证

 源码入口先从  dispatch  开始入口

    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进行封装
request = self.initialize_request(request, *args, **kwargs)
self.request = request
self.headers = self.default_response_headers # deprecate? try:
self.initial(request, *args, **kwargs) # Get the appropriate handler method
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(),
self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed response = handler(request, *args, **kwargs) except Exception as exc:
response = self.handle_exception(exc) self.response = self.finalize_response(request, response, *args, **kwargs)
return self.response

可以看出上面的代码中对原生的request进行了封装

request = self.initialize_request(request, *args, **kwargs)
self.request = request  

那么它内部做了些什么呢 ,追踪  self.initialize_request 代码如下:

    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
)

我们可以看到    authenticators=self.get_authenticators()  这个貌似和认证有关系,追踪get_authenticators 代码如下:

    def get_authenticators(self):
"""
Instantiates and returns the list of authenticators that this view can use.
"""
return [auth() for auth in self.authentication_classes]

通过上面的代码我们可以看到 其返回的是通过列表生成式返回的一个实例化对象    那么它是通过什么生成这个对象的呢,下面我们来继续追踪  self.authentication_classes 其代码如下所示:

class APIView(View):

    # The following policies may be set at either globally, or per-view.
renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
parser_classes = api_settings.DEFAULT_PARSER_CLASSES
authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES

到这里我们发现它是通过读取应用的配置文件,到这里神秘的面纱已经开始解开了,加入我们自己写的类如果自己的内部有  authentication_classes  这个属性,并且其也是一个可迭代的对象,那么根据面向对象的属性,就会执行我们

自己的self.authentication_classes  ,而不是默认到配置文件中去找。

对request的总结:

上面对原生的request对象进行了一些封装和获得了认证的实例话对象的列表

执行认证

继续追踪 dispatch  中的  self.initial  代码如下:

    def initial(self, request, *args, **kwargs):
"""
Runs anything that needs to occur prior to calling the method handler.
"""
self.format_kwarg = self.get_format_suffix(**kwargs) # Perform content negotiation and store the accepted info on the request
neg = self.perform_content_negotiation(request)
request.accepted_renderer, request.accepted_media_type = neg # Determine the API version, if versioning is in use.
version, scheme = self.determine_version(request, *args, **kwargs)
request.version, request.versioning_scheme = version, scheme # Ensure that the incoming request is permitted
self.perform_authentication(request)
self.check_permissions(request)
self.check_throttles(request)

根据英文的意思看以看到  self.perform_authentication 的意思是执行认证的意思,让我们追踪其内部的代码如下:

    def perform_authentication(self, request):
"""
Perform authentication on the incoming request. Note that if you override this and simply 'pass', then authentication
will instead be performed lazily, the first time either
`request.user` or `request.auth` is accessed.
"""
request.user

看到其调用的是对原始request封装后的user ,追踪其 内部的user发现其是一个实例属性,代码如下所示

Django rest framework 源码分析 (1)----认证

点进去可以看到Request有个user方法
    @property
def user(self):
"""
Returns the user associated with the current request, as authenticated
by the authentication classes provided to the request.
"""
if not hasattr(self, '_user'):
with wrap_attributeerrors():
self._authenticate()# 获取认证对象进行一步步的认证
return self._user

让我们进行追踪  self._authenticate()  代码如下:

    def _authenticate(self):
"""
Attempt to authenticate the request using each authentication instance
in turn.
"""
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()

执行认证类的authenticate方法

   这里分三种情况

     1.如果authenticate方法抛出异常,self._not_authenticated()执行后在抛出异常-----就是认证没通过

     2.有返回值,必须是元组:(request.user,request.auth)

     3.返回None,表示当前认证不处理,等下一个认证来处理

返回值就是例子中的:

token_obj.user-->>request.user
token_obj-->>request.auth

通过认证自定义的authenticated没有返回值,就执行self._not_authenticated(),相当于匿名用户,

def _not_authenticated(self):
"""
Set authenticator, user & authtoken representing an unauthenticated request. Defaults are None, AnonymousUser & None.
"""
self._authenticator = None if api_settings.UNAUTHENTICATED_USER:
self.user = api_settings.UNAUTHENTICATED_USER() #AnonymousUser匿名用户
else:
self.user = None if api_settings.UNAUTHENTICATED_TOKEN:
self.auth = api_settings.UNAUTHENTICATED_TOKEN() #None
else:
self.auth = None

因为authenticate方法我们自己重写了,所以当执行authenticate()的时候就是执行我们自己写的认证

父类中的authenticate方法

    def authenticate(self, request):
return (self.force_user, self.force_token)

我们自己写的

    def authenticate(self,request):
token = request._request.GET.get('token')
# 获取用户名和密码,去数据校验
if not token:
raise exceptions.AuthenticationFailed('用户认证失败')
return ("dog",None)

所以到这里我们就搞懂了我们在做用户认证的时候为什么需要重其authenticate的方法了

以上的是我们自己定义的 authentication_classes   它会去我们的类属性中使用我们自己的,只需在需要认证接口的中设置类属性 authentication_classes = [ 认证的逻辑函数 ]

全局认证

假如我们进行全局认证的话,可以在配置文件中设置,通过继承父类的方式来实现,简单的说就是 类属性 authentication_classes 自己不定义使用父类的,看以下的代码:

class APIView(View):

    # The following policies may be set at either globally, or per-view.
renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
parser_classes = api_settings.DEFAULT_PARSER_CLASSES
authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES

在以上的代码中我们可以看到authentication_classes = = api_settings.DEFAULT_AUTHENTICATION_CLASSES 我们可以追踪看到代码如下:

def reload_api_settings(*args, **kwargs):
setting = kwargs['setting']
if setting == 'REST_FRAMEWORK':
api_settings.reload() setting_changed.connect(reload_api_settings)

这是它会去配置文件中读取    REST_FRAMEWORK ,这时后我们可以在配置文件中添加这个配置,里面的键是 DEFAULT_PARSER_CLASSES 值是认证函数的路径

REST_FRAMEWORK = {
# 全局使用的认证类
"DEFAULT_AUTHENTICATION_CLASSES":['api.utils.auth.Authtication', ],
}

我们根据上述配置的路径,在应用api建立以下文件  utils/auth.py  代码如下:

class Authtication(BaseAuthentication):
def authenticate(self,request):
token = request._request.GET.get('token')
token_obj = models.UserToken.objects.filter(token=token).first()
if not token_obj:
raise exceptions.AuthenticationFailed('用户认证失败')
# 在rest framework内部会将整个两个字段赋值给request,以供后续操作使用
return (token_obj.user, token_obj) def authenticate_header(self, request):
pass

这时后所有的接口,必须经过认证后才能访问,但是有的接口是不需要认证的,比如登陆的时候这时,我们可以在登陆的接口中不走父类(apiview) 的认证使用我们自己 的,只需定义定义类属性authentication_classes = [ ] 即可

class AuthView(APIView):
"""
用于用户登录认证
"""
authentication_classes = [] def post(self,request,*args,**kwargs):
pass

关于匿名用户登陆的设置

有些时候用户是在没有登陆的时候,访问我们的接口,这时候我们称之为匿名的用户,我们可以对其进行简单的设置

    def _authenticate(self):
"""
Attempt to authenticate the request using each authentication instance
in turn.
"""
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() def _not_authenticated(self):
"""
Set authenticator, user & authtoken representing an unauthenticated request. Defaults are None, AnonymousUser & None.
"""
self._authenticator = None if api_settings.UNAUTHENTICATED_USER:
self.user = api_settings.UNAUTHENTICATED_USER()
else:
self.user = None if api_settings.UNAUTHENTICATED_TOKEN:
self.auth = api_settings.UNAUTHENTICATED_TOKEN()
else:
self.auth = None

在上面的源码中我们可以看到 匿名用户默认的request.user=AnonymousUser, request.auth = None ,我们来写一个接口来简单的认证以下

class UserInfoView(APIView):

    authentication_classes = []

    def get(self,request,*args,**kwargs):
print(request.user)
print(request.auth )
return HttpResponse('用户信息')

为其  配置url

 url(r'^api/v1/info/$', views.UserInfoView.as_view()),

在浏览器中输入

http://127.0.0.1:8000/api/v1/info/ 

测试的结果如下:

 Django rest framework 源码分析 (1)----认证

源码中的user 和token 的设置如下:

        if api_settings.UNAUTHENTICATED_USER:
self.user = api_settings.UNAUTHENTICATED_USER()
else:
self.user = None if api_settings.UNAUTHENTICATED_TOKEN:
self.auth = api_settings.UNAUTHENTICATED_TOKEN()

我们可以看到 对user 和 token 的设置,我们可以定义一个函数,所以我们可以这样设置,

REST_FRAMEWORK = {
# 全局使用的认证类
"DEFAULT_AUTHENTICATION_CLASSES":['api.utils.auth.FirstAuthtication','api.utils.auth.Authtication', ],
# "UNAUTHENTICATED_USER":lambda :"匿名用户",
"UNAUTHENTICATED_USER": None
"UNAUTHENTICATED_TOKEN":None,
}

测试的结果如下:

Django rest framework 源码分析 (1)----认证

drf的内置认证类

rest_framework里面内置了一些认证,我们自己写的认证类最好都要继承内置认证类 "BaseAuthentication"

from rest_framework.authentication import BasicAuthentication

BaseAuthentication源码:

class BaseAuthentication(object):
"""
All authentication classes should extend BaseAuthentication.
""" def authenticate(self, request):
"""
Authenticate the request and return a two-tuple of (user, token).
"""
#内置的认证类,authenticate方法,如果不自己写,默认则抛出异常
raise NotImplementedError(".authenticate() must be overridden.") def authenticate_header(self, request):
"""
Return a string to be used as the value of the `WWW-Authenticate`
header in a `401 Unauthenticated` response, or `None` if the
authentication scheme should return `403 Permission Denied` responses.
"""
#authenticate_header方法,作用是当认证失败的时候,返回的响应头
pass

其它内置认证类

rest_framework里面还内置了其它认证类,我们主要用到的就是BaseAuthentication,剩下的很少用到

Django rest framework 源码分析 (1)----认证

总结

自己写认证类方法梳理

(1)创建认证类

  • 继承BaseAuthentication    --->>1.重写authenticate方法;2.authenticate_header方法直接写pass就可以(这个方法必须写)

(2)authenticate()返回值(三种)

  • None ----->>>当前认证不管,等下一个认证来执行
  • raise exceptions.AuthenticationFailed('用户认证失败')       # from rest_framework import exceptions
  • 有返回值元祖形式:(元素1,元素2)      #元素1复制给request.user;  元素2复制给request.auth

(3)局部使用

  • authentication_classes = [BaseAuthentication,]

(4)全局使用

#设置全局认证
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES":['API.utils.auth.Authentication',]
}

  

Django rest framework 源码分析 (1)----认证的更多相关文章

  1. Django rest framework源码分析(一) 认证

    一.基础 最近正好有机会去写一些可视化的东西,就想着前后端分离,想使用django rest framework写一些,顺便复习一下django rest framework的知识,只是顺便哦,好吧. ...

  2. Django rest framework源码分析(1)----认证

    目录 Django rest framework(1)----认证 Django rest framework(2)----权限 Django rest framework(3)----节流 Djan ...

  3. Django rest framework源码分析(3)----节流

    目录 Django rest framework(1)----认证 Django rest framework(2)----权限 Django rest framework(3)----节流 Djan ...

  4. Django Rest Framework源码剖析(一)-----认证

    一.简介 Django REST Framework(简称DRF),是一个用于构建Web API的强大且灵活的工具包. 先说说REST:REST是一种Web API设计标准,是目前比较成熟的一套互联网 ...

  5. Django rest framework源码分析(4)----版本

    版本 新建一个工程Myproject和一个app名为api (1)api/models.py from django.db import models class UserInfo(models.Mo ...

  6. Django rest framework源码分析(2)----权限

    目录 Django rest framework(1)----认证 Django rest framework(2)----权限 Django rest framework(3)----节流 Djan ...

  7. Django Rest Framework源码剖析(八)-----视图与路由

    一.简介 django rest framework 给我们带来了很多组件,除了认证.权限.序列化...其中一个重要组件就是视图,一般视图是和路由配合使用,这种方式给我们提供了更灵活的使用方法,对于使 ...

  8. 3---Django rest framework源码分析(3)----节流

    Django rest framework源码分析(3)----节流 目录 添加节流 自定义节流的方法  限制60s内只能访问3次 (1)API文件夹下面新建throttle.py,代码如下: # u ...

  9. Django之REST framework源码分析

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

随机推荐

  1. hdu 1059 Dividing

    Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission( ...

  2. Scala 深入浅出实战经典 第43讲:主要介绍类型变量bound

    王家林亲授<DT大数据梦工厂>大数据实战视频 Scala 深入浅出实战经典(1-64讲)完整视频.PPT.代码下载:百度云盘:http://pan.baidu.com/s/1c0noOt6 ...

  3. 自动帮助创建android资源xml文件的网站

    自动帮助创建android资源xml文件的网站 http://android-holo-colors.com/ stack overflow上一个seekbar的例子: http://stackove ...

  4. Unity3D中的Coroutine详解

    Unity中的coroutine是通过yield expression;来实现的.官方脚本中到处会看到这样的代码. 疑问: yield是什么? Coroutine是什么? unity的coroutin ...

  5. &lbrack;MAC Eclipse&rsqb; Eclipse for MAC 中文乱码的解决办法

    笔者将在windows下的eclipse写的代码拷贝到MAC下,发现中文会出现乱码. 最初笔者遇到这个问题的时候,在网络上寻找了解决办法,出来的第一个网页(http://blog.csdn.net/w ...

  6. &lbrack;ERROR&rsqb; Failed to execute goal org&period;apache&period;maven&period;plugins&colon;maven-compiler-plugin&colon;3&period;1&colon;compile &lpar;default-compile&rpar; on project triage&colon; Compilation failure &lbrack;ERROR&rsqb; No compiler is provided in this environment&period;

    [ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.1:compile (default-c ...

  7. Lucene架构

    先整体上看一下Lucene的架构设计图(见下图),先看上层应用,首先是信息采集的过程,文件系统.数据库.万维网以及手工输入的文件都可以作为信息采集的对象,也是要搜索的文档的来源,采集万维网上的信息一般 ...

  8. &lbrack;转&rsqb;&lbrack;C&num;&rsqb;服务安装卸载命令

    c:\windows\microsoft.net\Framework\v2.0.50727\installutil.exe  [/u] X:\服务.exe

  9. 搜索jar包 出现很多 Artifact Id相同 但Group Id不同 的包

    举例:将传统本地 javassit-3.17.1-GA.jar包 改成maven引用,pom中 ---Add 输入javassit 搜索 这时候会搜索出超级多 Artifact Id相同 但Group ...

  10. Java overload和override的区别分析

    Java overload和override的区别分析 方法的重写(Overriding)和重载(Overloading)是Java多态性的不同表现.重写(Overriding)是父类与子类之间多态性 ...