这是我参与11月更文挑战的第4天,活动详情查看:2021最后一次更文挑战
一、认证过程源码剖析
1.先从请求说起,我们都知道在django(CBV)中,客户端的发来的请求会执行视图类的as_view方法,而as_view方法中会执行dispacth方法,然后在根据请求的类型执行相应的方法(get、post等)。
2.在上面的示例中,使用django rest framework中的视图类需要继承APIView,请求到达视图类会执行视图类的as_view方法,而OrderView中没有as_view()方法,所以执行APIView的as_view()方法,下面请看APIView类中的as_view()源码:
@classmethod
def as_view(cls, **initkwargs):
"""
Store the original class on the view function.
This allows us to discover information about the view when we do URL
reverse lookups. Used for breadcrumb generation.
"""if isinstance(getattr(cls, 'queryset', None), models.query.QuerySet):
def force_evaluation():
raise RuntimeError(
'Do not evaluate the `.queryset` attribute directly, ''as the result will be cached and reused between requests. ''Use `.all()` or call `.get_queryset()` instead.'
)
cls.queryset._fetch_all = force_evaluation
view = super(APIView, cls).as_view(**initkwargs) #执行父类as_view()方法
view.cls = cls
view.initkwargs = initkwargs
# Note: session based authentication is explicitly CSRF validated,# all other authentication is CSRF exempt.return csrf_exempt(view)
3.从以上源码中可以看到APIView中as_view又执行了父类的as_view方法,在看看APIView的父类是View类,这恰好是django中的view视图类,以下是View类中的as_view()的源码:
def as_view(cls, **initkwargs):
"""
Main entry point for a request-response process.
"""for key in initkwargs:
if key in cls.http_method_names:
raise TypeError("You tried to pass in the %s method name as a ""keyword argument to %s(). Don't do that."
% (key, cls.__name__))
if not hasattr(cls, key):
raise TypeError("%s() received an invalid keyword %r. as_view ""only accepts arguments that are already ""attributes of the class." % (cls.__name__, key))
def view(request, *args, **kwargs):
self = cls(**initkwargs)
if hasattr(self, 'get') and not hasattr(self, 'head'):
self.head = self.get
self.request = request
self.args = args
self.kwargs = kwargs
return self.dispatch(request, *args, **kwargs)
view.view_class = cls
view.view_initkwargs = initkwargs
# take name and docstring from class
update_wrapper(view, cls, updated=())
# and possible attributes set by decorators# like csrf_exempt from dispatch
update_wrapper(view, cls.dispatch, assigned=())
return view
4.从上源码可以看出View类的as_view()方法执行流程:验证请求方法--->返回view函数名称(view函数会执行dispatch方法),一旦有请求进来执行view函数-->执行dispatch方法
5.当APIView的as_view方法执行了父类的as_view方法以后,请求进来会执行view方法,view方法中会执行dispatch方法,而Oderview没有dispatch方法,所以执行父类(APIView)的dispatch方法,下面的APIView的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) #对django原始的request进行封装,返回Request对象(新的对象)。
self.request = request
self.headers = self.default_response_headers # deprecate?try:
self.initial(request, *args, **kwargs) #这里request参数实则是Request对象
# Get the appropriate handler methodif 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
6.从以上源码分析,执行APIView的dispatch方法时候会执行self.initialize_request方法,会对django原始的request进行封装。再看看initialize_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类,
request, #django原始的request对象,封装到Request中变成self._request
parsers=self.get_parsers(),
authenticators=self.get_authenticators(), #开始认证流程
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)
7.self.initialize_request()源码分析,实例化Request()类,封装原始的request,authenticators(认证),执行self.get_authenticators(),到了这里就开始django rest framework的认证流程,以下是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] #列表生成式返回认证类的对象列表
8.self.get_authenticators()源码分析,采用列表生成式,循环self.authentication_classes,实例化其中的每一个类,返回列表,不难发现authentication_classes属性正式我们在认证的时候用到认证类列表,这里会自动寻找该属性进行认证。倘若我们的视图类没有定义认证方法呢?,当然django rest framework 已经给我们加了默认配置,如果我们没有定义会自动使用settings中的DEFAULT_AUTHENTICATION_CLASSES作为默认(全局)下面是APIView类中的共有属性:
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
9.继续分析APIView的dispatch方法,此时执行self.inital方法,并将封装过后的request对象(Reuqest)作为参数进行传递,下面是self.inital()方法源码:
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)
10.在self.inital方法中会执行self.perform_authentication方法,而self.perform_authentication方法用会执行request.user,此时的request是Request对象,所以需分析Request类中的user属性,以下是Request部分类源码:
class Request(object):
"""
Wrapper allowing to enhance a standard `HttpRequest` instance.
Kwargs:
- request(HttpRequest). The original request instance.
- parsers_classes(list/tuple). The parsers to use for parsing the
request content.
- authentication_classes(list/tuple). The authentications used to try
authenticating the request's user.
"""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 #django原生的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
#### @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() #下hi ing
return self._user
11.从上源码分析,在Request对象中,user属性是一个属性方法,并会执行self._authentication方法,在继续看Request对象的self._authentication方法:
def _authenticate(self):
"""
Attempt to authenticate the request using each authentication instance
in turn.
"""for authenticator in self.authenticators:
try:
#执行认证类的authenticate方法
#这里分三种情况
#1.如果authenticate方法抛出异常,self._not_authenticated()执行
#2.有返回值,必须是元组:(request.user,request.auth)
#3.返回None,表示当前认证不处理,等下一个认证来处理 user_auth_tuple = authenticator.authenticate(self)
except exceptions.APIException:
self._not_authenticated()
raiseif user_auth_tuple is not None:
self._authenticator = authenticator
self.user, self.auth = user_auth_tuple #返回值对应示例中的token_obj.user和token_obj
return
self._not_authenticated()
12.从上源码分析,Request对象的self._authentication中循环self.authenticators(该列表是由认证对象构成的[对象1,对象2]),并执行每一个对象中的authenticate方法返回tuple,同时对该过程其进行了异常捕捉,有异常将返回给用户,下面是异常验证逻辑:
● 如果有异常则执行self._not_authenticated()方法,继续向上抛异常。
● 如果有返回值必须是一个元组,分别赋值给self.user, self.auth(request.user和request.auth),并跳出循环。
● 如果返回None,则由下一个循环处理,如果都为None,则执行self._not_authenticated(),返回 (AnonymousUser,None)
13.当都没有返回值,就执行self._not_authenticated(),相当于匿名用户,没有通过认证,并且此时django会返回默认的匿名用户设置AnonymousUser,如需要单独设置匿名用户返回值,则编写需要写UNAUTHENTICATED_USER的返回值:
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() #匿名用户配置,默认返回AnonymousUserelse:
self.user = None
if api_settings.UNAUTHENTICATED_TOKEN:
self.auth = api_settings.UNAUTHENTICATED_TOKEN() #Noneelse:
self.auth = None