一:使用RestFramwork,定义一个视图

from rest_framework.viewsets import ModelViewSet

class BookView(ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer

认证、频率和权限组件都由继承的ModelViewSet支持,所以要了解这三个组件的具体如何实现

对认证、频率、权限的管理就需要进入到其中查看

二:首先来了解组件认证

由上图可以看到ModelViewSet继承了六个类,前面的五个并没有组件的内容,不去管,接下来进入GenericViewSet类中看看

GenericViewSet这个类没有具体的代码,但是可以看到它继承了两个类ViewSetMixin,和generics.GenericAPIView

ViewSetMixin

这个类中主要需要了解的是as_view这个在url中使用的方法,这个类只是重写了as_view中的view方法,具体的核心代码如下

for method, action in actions.items():
handler = getattr(self, action)
setattr(self, method, handler)

简单来说,就是把url中传入的字典for循环,利用反射找到对应的方法重新设置get请求对应的函数

url(r'^authors/$', views.AuthorModelView.as_view({"get":"list","post":"create"})),

如上:在Django启动后,views.AuthorModelView.as_view({"get":"list","post":"create"})的执行结果是一个闭包函数view

请求发送进来,根据闭包函数外的actions:{"get":"list","post":"create"}设置self.get = list或者设置 self.post= create等等

由上可知,这个函数也与要找的组件关系不大。

generics.GenericAPIView

def get_queryset(self):
def get_object(self):
def get_serializer(self, *args, **kwargs):
def get_serializer_class(self):
def get_serializer_context(self):
def filter_queryset(self, queryset):
@property
def paginator(self):
def paginate_queryset(self, queryset):
def get_paginated_response(self, data):

类中方法与要找组件无关,继续进入其父类中找

在父类APIView中的dispach方法中

self.initial(request, *args, **kwargs)这一段代码负责所有的认证、权限和频率管理

因为视图的继承复杂,现在需要搞清楚类的继承关系和代码具体运行步骤,才好往下走

继承关系图

请求执行流程

Django启动

url(r'^authors/$', views.AuthorModelView.as_view({"get":"list","post":"create"})) 在Django启动时就执行,as_view的执行结果是一个闭包函数

view,由actions = {"get":"list","post":"create"}等参数包裹:

实际路由为:url(r'^authors/$', view)

请求到来:

根据继承关系:请求到来执行的view函数是类ViewSetMixin中的闭包函数view

view源代码

def view(request, *args, **kwargs):
self = cls(**initkwargs)
# We also store the mapping of request methods to actions,
# so that we can later set the action attribute.
# eg. `self.action = 'list'` on an incoming GET request.
self.action_map = actions # Bind methods to actions
# This is the bit that's different to a standard view
for method, action in actions.items():
handler = getattr(self, action)
setattr(self, method, handler) if hasattr(self, 'get') and not hasattr(self, 'head'):
self.head = self.get self.request = request
self.args = args
self.kwargs = kwargs # And continue as usual
return self.dispatch(request, *args, **kwargs)

可以看到,在将self.get,self.post等方法映射之后,view方法最终返回了self.dispatch(request, *args, **kwargs)的执行结果

根据对象的属性和方法查找原则,self.dispatchfan方法调用的是类APIView中的dispatch方法

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 = 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

dispatch的核心功能就是根据请求的方法不同,分发执行不同的代码,并最终返回结果。

在这里我注意到,每次请求分发之前都会执行self.initial(request, *args, **kwargs) 这段代码,也就是说每次请求都会进入这里执行。

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) # 频率

initial中的核心代码是依次执行:

self.perform_authentication(request) # 认证

self.check_permissions(request) # 权限

self.check_throttles(request) # 频率

也就是:认证通过才会验证权限,权限验证通过才会验证频率

perform_authentication源代码

def perform_authentication(self, request):
request.user

perform_authentication中返回了request.user,首先要明白这个request来自于哪里?

从dispatch中一路过来,request一直没做处理,说明request至少来自于dispatch,APIView中dispatch的源码中有一行代码可以解释request的来源

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

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
)

initialize_request代码中返回了一个Request类的对象,传入了

request
parsers=self.get_parsers(),
authenticators=self.get_authenticators(),
negotiator=self.get_content_negotiator(),
parser_context=parser_context

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 @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

perform_authentication代码中执行的request.user就是执行的Request类的user方法

user方法中的代码代码表示如果没有_user属性就执行self._authenticate()

 _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:for循环self.authenticators并赋值给authenticator,然后执行authenticate方法

首先要知道self.authenticators来自于哪里?

回溯代码:

_authenticate中调用了self.authenticators。

self对象来自于user方法

user方法中的self对象Request的实例化对象

Request的实例化对象的实例化对象有一个属性:

self.authenticators= authenticators or ()

authenticators 是一个Request类的实例化参数,默认为None,如果有传入参数则为传入的值

initialize_request源代码中实例化时:authenticators=self.get_authenticators(),

return Request(
request,
parsers=self.get_parsers(),
authenticators=self.get_authenticators(),
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)

这时的self来自于调用initialize_request的对象

initialize_request在dispatch中被调用,dispatch的调用对象即是自定义的视图类的实例化对象

也即使说self.get_authenticators()是视图类调用的get_authenticators方法

get_authenticators源代码

def get_authenticators(self):

    return [auth() for auth in self.authentication_classes]

get_authenticators中for循环视图类的authentication_classes的属性,加括号实例化组成一个列表返回

于是查找对象的属性,首先从对象自己找,然后从视图类中找,如果找不到,在依照继承关系从被继承的类中找

在被视图类所继承的类APIView中找到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
throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES
permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES
content_negotiation_class = api_settings.DEFAULT_CONTENT_NEGOTIATION_CLASS
metadata_class = api_settings.DEFAULT_METADATA_CLASS
versioning_class = api_settings.DEFAULT_VERSIONING_CLASS # Allow dependency injection of other settings to make testing easier.
settings = api_settings
api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS)
APISettings类中并没有DEFAULT_AUTHENTICATION_CLASSES属性,自动触发__getattr__方法
APISettings源码
class APISettings(object):

    def __init__(self, user_settings=None, defaults=None, import_strings=None):
if user_settings: # 如果user_settings有值执行下列代码
self._user_settings = self.__check_user_settings(user_settings)
self.defaults = defaults or DEFAULTS
# defaults有值则赋给self.defaults,没有则把DEFAULTS赋值给self.defaults
self.import_strings = import_strings or IMPORT_STRINGS
self._cached_attrs = set() @property
def user_settings(self):
if not hasattr(self, '_user_settings'): # 如果_user_settings没有定义
self._user_settings = getattr(settings, 'REST_FRAMEWORK', {})
# 从Django项目的settings文件中利用反射取出'REST_FRAMEWORK'的值赋给self._user_settings
return self._user_settings def __getattr__(self, attr): # 对象用.attr的方法查找不到属性时自动触发
if attr not in self.defaults: # 如果self.defaults中没有查找的属性则报错
raise AttributeError("Invalid API setting: '%s'" % attr) try:
# Check if present in user settings
val = self.user_settings[attr]
# 从self.user_settings执行返回的值中取出属性attr的的值赋给val
except KeyError:
# Fall back to defaults
val = self.defaults[attr] # Coerce import strings into classes
if attr in self.import_strings:
# 如果属性attr在self.import_strings中通过反射取出对应的相应的方法或属性做进一步处理
val = perform_import(val, attr) # Cache the result
self._cached_attrs.add(attr)
setattr(self, attr, val) # 利用反射给视图类对象设置一个属性attr值为val
return val
DEFAULTS = {
# Base API policies
'DEFAULT_RENDERER_CLASSES': (
'rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.BrowsableAPIRenderer',
),
'DEFAULT_PARSER_CLASSES': (
'rest_framework.parsers.JSONParser',
'rest_framework.parsers.FormParser',
'rest_framework.parsers.MultiPartParser'
),
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication'
),
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.AllowAny',
),
'DEFAULT_THROTTLE_CLASSES': (),
'DEFAULT_CONTENT_NEGOTIATION_CLASS': 'rest_framework.negotiation.DefaultContentNegotiation',
'DEFAULT_METADATA_CLASS': 'rest_framework.metadata.SimpleMetadata',
'DEFAULT_VERSIONING_CLASS': None, # Generic view behavior
'DEFAULT_PAGINATION_CLASS': None,
'DEFAULT_FILTER_BACKENDS': (), # Schema
'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.AutoSchema', # Throttling
'DEFAULT_THROTTLE_RATES': {
'user': None,
'anon': None,
},
'NUM_PROXIES': None, # Pagination
'PAGE_SIZE': None, # Filtering
'SEARCH_PARAM': 'search',
'ORDERING_PARAM': 'ordering', # Versioning
'DEFAULT_VERSION': None,
'ALLOWED_VERSIONS': None,
'VERSION_PARAM': 'version', # Authentication
'UNAUTHENTICATED_USER': 'django.contrib.auth.models.AnonymousUser',
'UNAUTHENTICATED_TOKEN': None, # View configuration
'VIEW_NAME_FUNCTION': 'rest_framework.views.get_view_name',
'VIEW_DESCRIPTION_FUNCTION': 'rest_framework.views.get_view_description', # Exception handling
'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler',
'NON_FIELD_ERRORS_KEY': 'non_field_errors', # Testing
'TEST_REQUEST_RENDERER_CLASSES': (
'rest_framework.renderers.MultiPartRenderer',
'rest_framework.renderers.JSONRenderer'
),
'TEST_REQUEST_DEFAULT_FORMAT': 'multipart', # Hyperlink settings
'URL_FORMAT_OVERRIDE': 'format',
'FORMAT_SUFFIX_KWARG': 'format',
'URL_FIELD_NAME': 'url', # Input and output formats
'DATE_FORMAT': ISO_8601,
'DATE_INPUT_FORMATS': (ISO_8601,), 'DATETIME_FORMAT': ISO_8601,
'DATETIME_INPUT_FORMATS': (ISO_8601,), 'TIME_FORMAT': ISO_8601,
'TIME_INPUT_FORMATS': (ISO_8601,), # Encoding
'UNICODE_JSON': True,
'COMPACT_JSON': True,
'STRICT_JSON': True,
'COERCE_DECIMAL_TO_STRING': True,
'UPLOADED_FILES_USE_URL': True, # Browseable API
'HTML_SELECT_CUTOFF': 1000,
'HTML_SELECT_CUTOFF_TEXT': "More than {count} items...", # Schemas
'SCHEMA_COERCE_PATH_PK': True,
'SCHEMA_COERCE_METHOD_NAMES': {
'retrieve': 'read',
'destroy': 'delete'
},
} DEFAULTS

DEFAULT

在本例中视图类中并没有重写authentication_classes,因此根据APISettings中的代码可知,程序首先在Django的settings文件中查找,由于settins文件中没有定义,因此抛出异常,最终从DEFAULTS中取得了authentication_classes的值

最终APIView中authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES的执行结果是

authentication_classes =
(
SessionAuthentication,
BasicAuthentication
),

于是

authenticators = [SessionAuthentication(),BasicAuthentication()]

最终在 _authenticate源代码中执行的是SessionAuthentication,BasicAuthentication这两个方法中的authenticate(self, request)方法

class SessionAuthentication(BaseAuthentication):
"""
Use Django's session framework for authentication.
""" 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) def enforce_csrf(self, request):
"""
Enforce CSRF validation for session based authentication.
"""
reason = CSRFCheck().process_view(request, None, (), {})
if reason:
# CSRF failed, bail with explicit error message
raise exceptions.PermissionDenied('CSRF Failed: %s' % reason)
authenticate方法的逻辑就是就是认证组件的实际逻辑
根据整个源码的思路,可以在重新写一个认证类,而其中必定有authenticate方法来控制验证逻辑
from rest_framework.exceptions import AuthenticationFailed
from rest_framework.authentication import BaseAuthentication class TokenAuth(BaseAuthentication): def authenticate(self,request): token=request.GET.get("token",None) token_obj=UserToken.objects.filter(token=token).first()
if token_obj:
return token_obj.user.user,token_obj
else:
raise AuthenticationFailed("认证失败!")
class BookView(ModelViewSet):
# 指定认证类
authentication_classes = [TokenAuth]
queryset = Book.objects.all()
serializer_class = BookSerializer

三、权限组件源码剖析

权限组件大部分与认证组件执行流程相似,在下面仅列出不同之处,源码如下:

    def initial(self, request, *args, **kwargs):
#认证组件
self.perform_authentication(request)
#权限组件
self.check_permissions(request)
#频率组件
self.check_throttles(request)
def check_permissions(self, request):
"""
Check if the request should be permitted.
Raises an appropriate exception if the request is not permitted.
"""
for permission in self.get_permissions(): #self.get_permissions() = permission_classes
        if not permission.has_permission(request, self): 
          self.permission_denied( request, message=getattr(permission, 'message', None) )
根据整个源码的思路,可以在重新写一个权限类,而其中必定有has_permission方法来控制验证逻辑
class BookView(ModelViewSet):
# 指定认证类
authentication_classes = [UserAuth]
permission_classes = [UserPerm]
throttle_classes = [MyThrottle]
queryset = Book.objects.all()
serializer_class = BookSerializer
class UserPerm():
message = "您没有查看该数据的权限!" def has_permission(self, request, view):
if request.user.user_type == 3:
return True
return False

Rest_Framework之认证、权限、频率组件源码剖析的更多相关文章

  1. Element 2 组件源码剖析之布局容器

    0x00 简介 前文分析过组件的 布局栅格化(Grid Layout) ,通过基础的 24 分栏,迅速简便地创建布局. 本文将介绍用于布局的容器组件,使用 Flexbox 功能将其所控制区域设定为特定 ...

  2. Django的rest_framework的权限组件和频率组件源码分析

    前言: Django的rest_framework一共有三大组件,分别为认证组件:perform_authentication,权限组件:check_permissions,频率组件:check_th ...

  3. Django框架之DRF 认证组件源码分析、权限组件源码分析、频率组件源码分析

    认证组件 权限组件 频率组件

  4. Django REST framework认证权限和限制 源码分析

    1.首先 我们进入这个initial()里面看下他内部是怎么实现的. 2.我们进入里面看到他实现了3个方法,一个认证,权限频率 3.我们首先看下认证组件发生了什么 权限: 啥都没返回,self.per ...

  5. DRF-认证 权限 频率组件

    补充 1 认证 权限 频率组件原理基本相同 2 认证相关: session cookie token 认证相关的  这里用token token 1 有时间限制,超时则失效 2 每次登录更换一个tok ...

  6. 06 drf源码剖析之权限

    06 drf源码剖析之权限 目录 06 drf源码剖析之权限 1. 权限简述 2. 权限使用 3.源码剖析 4. 总结 1. 权限简述 权限与身份验证和限制一起,决定了是否应授予请求访问权限. 权限检 ...

  7. 05 drf源码剖析之认证

    05 drf源码剖析之认证 目录 05 drf源码剖析之认证 1. 认证简述 2. 认证的使用 3. 源码剖析 4. 总结 1. 认证简述 当我们通过Web浏览器与API进行交互时,我们可以登录,然后 ...

  8. drf源码剖析系列(系列目录)

    drf源码剖析系列(系列目录) 01 drf源码剖析之restful规范 02 drf源码剖析之快速了解drf 03 drf源码剖析之视图 04 drf源码剖析之版本 05 drf源码剖析之认证 06 ...

  9. Django-restframework 源码之认证组件源码分析

    Django-restframework 源码之认证组件源码分析 一 前言 之前在 Django-restframework 的流程分析博客中,把最重要的关于认证.权限和频率的方法找到了.该方法是 A ...

随机推荐

  1. Spring 梳理-el表达式和jstl

    JSP中有这么几种元素 1: Scriptlet <% ... %> 2: 声明元素 <%! ... %> 3: Java表达式 <%= ... %> 4: 指令元 ...

  2. 猿说python

    一.简介         知识改变命运,程序改变世界.互联网时代潜移默化的改变着我们的生活,伴随技术的进步,我想下一个时代应该属于人工智能和机器学习,属于python.           pytho ...

  3. 夯实Java基础系列18:深入理解Java内部类及其实现原理

    本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutorial 喜欢的话麻烦点下 ...

  4. python爬虫—— 抓取今日头条的街拍的妹子图

    AJAX 是一种用于创建快速动态网页的技术. 通过在后台与服务器进行少量数据交换,AJAX 可以使网页实现异步更新.这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新. 近期在学习获取j ...

  5. 03-Django基础概念和MVT架构

    一.Django基础 掌握Django的 MVT 架构的使用 掌握Git管理源代码 主要内容 了解Django的 MVT 架构的使用流程 使用Django完成案例 : 书籍信息管理 MVC介绍 MVC ...

  6. Solidity 编程实例--投票

    Voting 投票 思路是为每张选票创建一个合约,每个投票选项提供一个短名称.合约创建者作为会长将会给每个投票参与人各自的地址投票权. 地址后面的人们可以选择自己投票或者委托信任的代表人替他们投票.在 ...

  7. redis系列之------字典

    前言 字典, 又称符号表(symbol table).关联数组(associative array)或者映射(map), 是一种用于保存键值对(key-value pair)的抽象数据结构. 在字典中 ...

  8. A-03 牛顿法和拟牛顿法

    目录 牛顿法和拟牛顿法 一.牛顿法详解 1.1 无约束最优化问题 1.2 牛顿法迭代公式 1.3 牛顿法和梯度下降法 二.牛顿法流程 2.1 输入 2.2 输出 2.3 流程 三.拟牛顿法简介 更新. ...

  9. SVM面试知识点总结

    1. SVM 原理 SVM 是一种二类分类模型.它的基本思想是在特征空间中寻找间隔最大的分离超平面使数据得到高效的二分类,具体来讲,有三种情况(不加核函数的话就是个线性模型,加了之后才会升级为一个非线 ...

  10. conversion function——转换函数

    类型转换函数 与 explicit关键字 1.类型转换函数 在C++中,可以使用构造函数将一个指定类型的数据转换为类的对象,也可以使用类型转换函数 (type conversion function) ...