在 APIview 类中的属性有一条是:

authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES

定义了一个类属性为authentication_classes,值从api_settings中的一个属性,查看api_settings发现也是个类对象,

api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS)

是有 APISettings 实例化出来的一个对象,传入了三个参数,重点为后面两个参数

  1. DEFAULTS是一个字典,其中有一个 key为DEFAULT_AUTHENTICATION_CLASSES,对应的值为一个元祖:

    (

    'rest_framework.authentication.SessionAuthentication',

    'rest_framework.authentication.BasicAuthentication'

    )

  2. IMPORT_STRINGS 为一个元祖,里面也包含有DEFAULT_AUTHENTICATION_CLASSES,这个元祖是一些列的设置参数通过字符串的形式导入(可能采用字符串导入表示法的设置列表。)

将这三个参数传到 APISettings 中进行初始化:

def init(self, user_settings=None, defaults=None, import_strings=None):

if user_settings:

self._user_settings = self.__check_user_settings(user_settings)

self.defaults = defaults or DEFAULTS

self.import_strings = import_strings or IMPORT_STRINGS

self._cached_attrs = set()

将对象赋值给 api_settings,之后通过该对象获取相关属性。

那么在 APIView 中是如何获取到 api_settings.DEFAULT_AUTHENTICATION_CLASS的呢?

去 APISettings 类中查找该属性,发现并没有该属性,但是具有__getattr__方法,该方法接收一个属性名,返回该属性的值,具体如下:

def getattr(self, attr):

if attr not in self.defaults:

raise AttributeError("Invalid API setting: '%s'" % attr)

try:
# Check if present in user settings
val = self.user_settings[attr]
except KeyError:
# Fall back to defaults
val = self.defaults[attr] # Coerce import strings into classes
if attr in self.import_strings:
val = perform_import(val, attr) # Cache the result
self._cached_attrs.add(attr)
setattr(self, attr, val)
return val

在 try 中执行self.user_settings[attr],是个类方法,使用property装饰器装饰为属性:

@property

def user_settings(self):

if not hasattr(self, '_user_settings'):

self._user_settings = getattr(settings, 'REST_FRAMEWORK', {})

return self._user_settings

该方法里面经过反射判断, self中是否含有_user_settings,因为在实例化该对象时,传入的三个参数第一个为 None,所以在 init 方法中self 没有该属性,所以执行self._user_settings = getattr(settings, 'REST_FRAMWORK', {}),该 settings 是个模块,该模块在执行项目的时候会加载全局配置和自定义配置,而在自定义配置中的INSTALLED_APPS中注册了rest_framework,所以可以返回需要寻找的值,而在下面的 if attr in self.import_strings:,我前面特意说了第三个参数里面也有DEFAULT_AUTHENTICATION_CLASSES,所以这个判断也成立,执行里面的val = perform_import(val, attr)方法,方法如下:

def perform_import(val, setting_name):

"""

If the given setting is a string import notation,

then perform the necessary import or imports.

"""

if val is None:

return None

elif isinstance(val, six.string_types):

return import_from_string(val, setting_name)

elif isinstance(val, (list, tuple)):

return [import_from_string(item, setting_name) for item in val]

return val

该方法把之前获取的 val 当做参数传进去,在里面进行 val 的类型判断,该 val 是一个列表,于是执行下面的方法,列表生成式生成一个列表,之后把结果进行缓存(把该 attr 加到一个集合中),之后给 api_settings对象设置一个 key 为 attr,value 为 val 的属性,并将 val 返回。这个值根据后续的执行判断是一个列表,列表里面有两个类的内存地址,分别是[SessionAuthentication, BasicAuthentication],

在return [import_from_string(item, setting_name) for item in val]该列表生成式中,item 是上面列表中的类,setting_name是DEFAULT_AUTHENTICATION_CLASSES,

最终结果就是将这两个类导入到内存中,也就是[SessionAuthentication, BasicAuthentication],上面那个列表里面是类似于['rest_framework.authentication.SessionAuthentication', 'rest_framework.authentication.BasicAuthentication']这样的。

最终得到的结果为:

authentication_classes = [SessionAuthentication, BasicAuthentication]。

之后再 APIView 中的 dispatch 方法中执行到 self.initial时,把经过 Request 类封装产生的对象和相关参数传进去,

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)

这个方法可以说是 restframework中最重要的方法了,因为这里面有认证、权限以及频率的校验。

  1. 先去self.perform_authentication方法中看。

    该方法将Request 对象当做参数传进去,在该方法里面只有一行代码:

    request.user,这个方法是 Request 对象的方法,于是去 Request 类中寻找。

    request.user是经过 property 装饰器装饰成的数据属性,

    在该方法里面的代码如下:

    @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, 也就是 request.user的 request 是否有_user这个属性,在 Request 类中的初始化方法中寻找,代码如下:

def init(self, request, parsers=None, authenticators=None,

negotiator=None, parser_context=None):

assert isinstance(request, HttpRequest), (

'The request argument must be an instance of '

'django.http.HttpRequest, not {}.{}.'

.format(request.class.module, request.class.name)

)

self._request = request
self.parsers = parsers or ()
self.authenticators = authenticators or ()
self.negotiator = negotiator or self._default_negotiator()
self.parser_context = parser_context
self._data = Empty
self._files = Empty
self._full_data = Empty
self._content_type = Empty
self._stream = Empty if self.parser_context is None:
self.parser_context = {}
self.parser_context['request'] = self
self.parser_context['encoding'] = request.encoding or settings.DEFAULT_CHARSET force_user = getattr(request, '_force_auth_user', None)
force_token = getattr(request, '_force_auth_token', None)
if force_user is not None or force_token is not None:
forced_auth = ForcedAuthentication(force_user, force_token)
self.authenticators = (forced_auth,)

该对象中没有_user属性,进入到_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()

在该方法中,之前花了大力气找到的authentication_classes = [SessionAuthentication, BasicAuthentication]可以派上用场了,

for authenticator in self.authenticators:

self.authenticators是 request 对象的一个属性,在初始化方法中,

self.authenticators = authenticators or {},这个 authenticators 是实例化 Request 对象时传进来的参数,回到APIView 中的 dispatch 方法里面的 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
)

在实例化 Request 对象时,传进了五个参数,一个是原生 request 对象,重点关注authenticators = self.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,这是 APIView 类中的一个方法,查看源码,这个self.authentication_classes就是一个类属性,就是authentication_classes,所以获取到这个属性对应的值为[SessionAuthentication, BasicAuthentication],

循环该列表,因为该列表中的对象是类,所以加括号就是实例化产生对应的对象。之后再_authenticate方法中循环该列表,就是一个个类对象并调用类对象的authenticate(self)方法,将 Request 对象作为参数传进去,

现在找具体生成的类对象,用SessionAuthentication

authenticate 方法如下:

def authenticate(self, request):

"""

Returns a User if the request session currently has a logged in user.

Otherwise returns None.

"""

# Get the session-based user from the underlying HttpRequest object
user = getattr(request._request, 'user', None) # Unauthenticated, CSRF validation not required
if not user or not user.is_active:
return None self.enforce_csrf(request) # CSRF passed with authenticated user
return (user, None)

该方法的参数就是经过 Request 实例化产生的 request,该方法就是具体的认证方法,

当 request._request.user不为空是,执行self.enforce_csrf方法,具体代码如下:

def enforce_csrf(self, request):

"""

Enforce CSRF validation for session based authentication.

"""

check = CSRFCheck()

# populates request.META['CSRF_COOKIE'], which is used in process_view()

check.process_request(request)

reason = check.process_view(request, None, (), {})

if reason:

# CSRF failed, bail with explicit error message

raise exceptions.PermissionDenied('CSRF Failed: %s' % reason)

在该方法里面实例化了一个 CSRFCheck对象,mro 列表查到,在它的父父类中找到 init 方法,然后调用check.process_request方法,这个方法很眼熟,就是出中间件request的方法,具体代码如下(也是通过 mro 列表查到的):

def process_request(self, request):

csrf_token = self._get_token(request)

# print(csrf_token, '*****')

if csrf_token is not None:

# Use same token next time.

request.META['CSRF_COOKIE'] = csrf_token

该方法是处理 csrf_token的,首先执行self._get_token(request)方法,获取 token,具体代码如下:

def _get_token(self, request):

if settings.CSRF_USE_SESSIONS:

try:

return request.session.get(CSRF_SESSION_KEY)

except AttributeError:

raise ImproperlyConfigured(

'CSRF_USE_SESSIONS is enabled, but request.session is not '

'set. SessionMiddleware must appear before CsrfViewMiddleware '

'in MIDDLEWARE%s.' % ('_CLASSES' if settings.MIDDLEWARE is None else '')

)

else:

try:

cookie_token = request.COOKIES[settings.CSRF_COOKIE_NAME]

except KeyError:

return None

    csrf_token = _sanitize_token(cookie_token)
if csrf_token != cookie_token:
# Cookie token needed to be replaced;
# the cookie needs to be reset.
request.csrf_cookie_needs_reset = True
return csrf_token

首先进行 if settings.CSRF_USE_SESSIONS判断,settings是由 LazySettings 类产生的一个对象,查找 init 方法,在父类LazyObject中找到 init 方法,代码如下:

def init(self):

# Note: if a subclass overrides init(), it will likely need to

# override copy() and deepcopy() as well.

self._wrapped = empty

if settings.CSRF_USE_TOKEN,因为该类中以及父类中没有该属性,但是在 LazySettings中有__getattr__方法,于是会调用该方法来获取属性值,代码如下:

def getattr(self, name):

"""

Return the value of a setting and cache it in self.dict.

"""

if self._wrapped is empty:

self._setup(name)

val = getattr(self._wrapped, name)

self.dict[name] = val

return val

该函数里面会判断self._wrapped是否为空对象,因为在父类 LazyObject 中的 init 方法中设置了self._wrapped = empty,所以判断为 True,会执行self._setup(name),该 name 是要查找的属性名,在这里也就是 CSRF_USE_SESSIONS,_setup代码如下:

def _setup(self, name=None):

"""

Load the settings module pointed to by the environment variable. This

is used the first time we need any settings at all, if the user has not

previously configured the settings manually.

"""

settings_module = os.environ.get(ENVIRONMENT_VARIABLE)

if not settings_module:

desc = ("setting %s" % name) if name else "settings"

raise ImproperlyConfigured(

"Requested %s, but settings are not configured. "

"You must either define the environment variable %s "

"or call settings.configure() before accessing settings."

% (desc, ENVIRONMENT_VARIABLE))

self._wrapped = Settings(settings_module)

settings_module = os.environ.get(ENVIRONMENT_VARIABLE)是为了获取相关配置模块,然后生成 Settings(settings_module)对象赋值给 self._wrapped,所以 self._wrapped是一个 Settings 对象,所以在执行val = getattr(self._wrapped, name)时就是在该 Settings 中找到属性名为CSRF_USE_TOKEN的值,在 django.conf.settings的全局配置中该值为 False,所以会走下面的 else 代码块。具体如下(_get_token方法):

else:

try:

cookie_token = request.COOKIES[settings.CSRF_COOKIE_NAME]

except KeyError:

return None

    csrf_token = _sanitize_token(cookie_token)
if csrf_token != cookie_token:
# Cookie token needed to be replaced;
# the cookie needs to be reset.
request.csrf_cookie_needs_reset = True
return csrf_token

在 try 中会通过封装的 Request 对象来获取 cookie,在全局配置中,CSRF_COOKIE_NAME的值为

csrftoken,会把这个值当做 key 来查找值并赋值给cookie_token,之后执行该函数,代码如下:

def _sanitize_token(token):

# Allow only ASCII alphanumerics

if re.search('[^a-zA-Z0-9]', force_text(token)):

return _get_new_csrf_token()

elif len(token) == CSRF_TOKEN_LENGTH:

return token

elif len(token) == CSRF_SECRET_LENGTH:

# Older Django versions set cookies to values of CSRF_SECRET_LENGTH

# alphanumeric characters. For backwards compatibility, accept

# such values as unsalted secrets.

# It's easier to salt here and be consistent later, rather than add

# different code paths in the checks, although that might be a tad more

# efficient.

return _salt_cipher_secret(token)

return _get_new_csrf_token()

Django-restframework源码分析笔记的更多相关文章

  1. zeromq源码分析笔记之线程间收发命令(2)

    在zeromq源码分析笔记之架构说到了zmq的整体架构,可以看到线程间通信包括两类,一类是用于收发命令,告知对象该调用什么方法去做什么事情,命令的结构由command_t结构体确定:另一类是socke ...

  2. ReentrantReadWriteLock源码分析笔记

    ReentrantReadWriteLock包含两把锁,一是读锁ReadLock, 此乃共享锁, 一是写锁WriteLock, 此乃排它锁. 这两把锁都是基于AQS来实现的. 下面通过源码来看看Ree ...

  3. ArrayList源码分析笔记

    ArrayList源码分析笔记 先贴出ArrayList一些属性 public class ArrayList<E> extends AbstractList<E> imple ...

  4. $Django cbv源码分析 djangorestframework框架之APIView源码分析

    1 CBV的源码分析 #视图 class login (View): pass #路由 url(r'^books/$', views.login.as_view()) #阅读源码: #左侧工程栏--- ...

  5. ROCKETMQ源码分析笔记1:tools

    rocketmq源码解析笔记 大家好,先安利一下自己,本人男,35岁,已婚.目前就职于小资生活(北京),职位是开发总监. 姓名DaneBrown 好了.我保证本文绝不会太监!转载时请附上以上安利信息. ...

  6. django --- DetailView源码分析

    [背景] 最近在看django官方文档的class-based-views这一节的时候一直不得要领,感觉自己清楚,但是回想起来又没有脉络:于是没有办法只 能是“暗中观察”django的源码了. 刚打开 ...

  7. 线程池之ThreadPoolExecutor线程池源码分析笔记

    1.线程池的作用 一方面当执行大量异步任务时候线程池能够提供较好的性能,在不使用线程池的时候,每当需要执行异步任务时候是直接 new 一线程进行运行,而线程的创建和销毁是需要开销的.使用线程池时候,线 ...

  8. Android源码分析笔记--Handler机制

    #Handler机制# Handler机制实际就是实现一个 异步消息循环处理器 Handler的真正意义: 异步处理 Handler机制的整体表述: 消息处理线程: 在Handler机制中,异步消息处 ...

  9. Django——admin源码分析

    在Django中,如果我们新建一个项目,只要在admin.py文件中注册,就可以对其相应的文件进行增删改查操作. 而我们在路由系统中只看到了一条信息:url(r'^admin/', admin.sit ...

随机推荐

  1. 浏览器url地址殊字符转义编码

    网址URL中特殊字符转义编码字符    -    URL编码值 空格    -    %20"          -    %22#         -    %23%        -   ...

  2. 使用Spring Aop自定义注解实现自动记录日志

    百度加自己琢磨,以下亲测有效,所以写下来记录,也方便自己回顾浏览加深印象之类,有什么问题可以评论一起解决,不完整之处也请大佬指正,一起进步哈哈(1)首先配置文件: <!-- 声明自动为sprin ...

  3. typeScript 学习

    最近看了下typescript, 虽然说已经有很多人已经用到它了,但是我还是写写自己的feel咯:这里推荐学习链接 https://ts.xcatliu.com. 这个入门学习,我不好做评价,但是我自 ...

  4. Linux的小知识点

    uname 2.whereis 3.df 4.which 5.apt和dpkg 6.service 7./etc/init.d/ 8.netstat -anptu 查看端口占用 9.netstat 1 ...

  5. Python-数据类型1

    在Python中常见的数据类型有:整数(int).字符串(str).小数/浮点数(float).列表.元组.字典和布尔类型等,下面会进行一一介绍. 整数和小数,不用多介绍相信大家都有所了解,字符串是用 ...

  6. 折线图hellocharts的使用说明

    以前用过一次XCL-chart,但是感觉只适合固定图表,不去滑动的那种,因为你一滑动太卡了你懂得(毕竟作者好久没更新优化了),拙言大神我开玩笑的 ,毕竟我加你的群大半年了 - - 第二研究了一下ach ...

  7. JavaEE开发之Spring中的条件注解、组合注解与元注解

    上篇博客我们详细的聊了<JavaEE开发之Spring中的多线程编程以及任务定时器详解>,本篇博客我们就来聊聊条件注解@Conditional以及组合条件.条件注解说简单点就是根据特定的条 ...

  8. [Swift]LeetCode217. 存在重复元素 | Contains Duplicate

    Given an array of integers, find if the array contains any duplicates. Your function should return t ...

  9. [Swift]LeetCode916.单词子集 | Word Subsets

    We are given two arrays A and B of words.  Each word is a string of lowercase letters. Now, say that ...

  10. [Swift]LeetCode920. 播放列表的数量 | Number of Music Playlists

    Your music player contains N different songs and she wants to listen to L (not necessarily different ...