一:使用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. springmvc(二)

    请求信息转换 异步发送表单数据到JavaBean,并响应JSON文本返回 操作步骤:(1)加入Jackson2或fastjson框架包,springmvc默认支持Jackon2,不需要做任何操作,而f ...

  2. freemarker常用属性

    1.th:action 定义后台控制器的路径,类似<form>标签的action属性. 示例如下. <form id="login" th:action=&quo ...

  3. CSS3 transform属性

    说明: transform 属性向元素应用 2D 或 3D 转换.该属性允许我们对元素进行移动(translate).旋转(rotate).缩放(scale)或倾斜(skew) transition属 ...

  4. 完美实现保存和加载easyui datagrid自定义调整列宽位置隐藏属性功能

    需求&场景 例表查询是业务系统中使用最多也是最基础功能,但也是调整最平凡,不同的用户对数据的要求也不一样,所以在系统正式使用后,做为开发恨不得坐在业务边上,根据他们的要求进行调整,需要调整最多 ...

  5. IDEA 学习笔记之 Maven项目开发

    Maven项目开发: 配置Maven: 新建Maven项目: 选择webapp: 和eclipse一样,设置: 修改maven配置,添加一个新属性,可以加快项目创建速度: 完成: 新建java和tes ...

  6. 【maven的使用】1maven的概念与配置

    maven是一个基于java平台的自动化构建工具.构建工具的发展由make->ant->maven->gradle其中gradle还在发展中,使用较少,学习难度比较大,所以目前占据主 ...

  7. Github配合Jenkins,实现vue等前端项目的自动构建与发布

    本篇文章前端项目以vue为例(其实前端工程化项目的操作方法都相同),部署在Linux系统上(centos). 之前做前端项目的部署,一直都是手动运行打包命令,打包完.再使用FTP.Xshell等这类的 ...

  8. mac系统Intellij Idea的java环境配置:JDK + Tomcat + Maven

    一.JAVA JDK查看与配置 1.查看java路径详细信息: /usr/libexec/java_home -V 2.java默认路径 jdk1.6: /System/Library/Java/Ja ...

  9. oracle查询当前用户下所有的表,包括所有的字段

    oracle查询当前用户下所有的表,包括所有的字段 背景: ​ 前两天接到一个需求,做一个展示所有表名,表备注,表数据,表字段数,点击查看按钮查看字段名和注释,支持导出. 在Oracle中,可用使用视 ...

  10. C# Foreach循环本质与枚举器

    对于C#里面的Foreach学过 语言的人都知道怎么用,但是其原理相信很多人和我一样都没有去深究.刚回顾泛型讲到枚举器让我联想到了Foreach的实现,所以进行一番探究,有什么不对或者错误的地方大家多 ...