框架之认证接口的源码分析及自定义接口的使用
目录
rest_framework框架之认证的使用和源码实现流程
一、认证功能的源码流程
(一)、创建视图函数
Note
创建视图函数后,前端发起请求,url分配路由,执行视图类,视图类中执行对应方法必须经过dispatch()即调度方法from rest_framework.views import APIView
from django.shortcuts import HttpResponse
import jsonclass DogView(APIView):
def get(self, request, *args, **kwargs):
result = {
‘code‘: ‘10000‘,
‘msg‘: ‘数据创建成功‘
}
return HttpResponse(json.dumps(result))def post(self, request, *args, **kwargs): return HttpResponse('创建一条订单') def put(self, request, *args, **kwargs): return HttpResponse('更新一条订单') def delete(self, request, *args, **kwargs): return HttpResponse('删除一条订单')
(二)、运行dispatch方法
Note
如果自己定义了dispatch方法,则程序运行自定义方法,如果没有,程序运行源码中的dispatch方法。从dispatch方法中可以找到原生request在作为参数传递后被initialize_request()函数进行了加工,通过加工的request获得的值包括原生的request和BaseAuthentication实例化对象,所以我们需要找到initialize_request()。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)
‘‘‘
对原生的request进行加工,获得到的request已经不是原来的request,还包括了其他的参数,
可以通过新的request获取到内部包含的参数
加工后的request : Restquest(
request,
parsers=self.get_parsers(),
authenticators=self.get_authenticators(),negotiator=self.get_content_negotiator(), parser_context=parser_context )) ''' self.request = request self.headers = self.default_response_headers # deprecate? try: self.initial(request, *args, **kwargs) # 把加工后的request当作参数传递给了initial()函数 # 需要把在这里查找initial()函数 # 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
查看initialize_request()函数
Note
在initialize_request()函数中返回了authenticators, 通过观察可以看出,authenticators的值来自于另外一个函数get_authenticators()。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(), # authenticators获取到的是实例化后的类对象列表,即[Foo(), Bar()] negotiator=self.get_content_negotiator(), parser_context=parser_context )
找到函数self.get_authenticators()
Note
这个函数中实质上是把一个认证类列表实例化为对象列表进行返回,这里就可以得出在上一个驶入函数中的authenticators是一个实例化对象列表。需要继续往源头找,查找authentication_classesdef get_authenticators(self):
"""
Instantiates and returns the list of authenticators that this view can use.
"""
# 例如self.authentication_classes = [foo, bar]
return [auth() for auth in self.authentication_classes]
# 列表生成式,auth获取到的是列表中的类,auth()是把获取到的类对象进行实例化操作查找authentication_classes类
Note
在自己编写的代码中并没有定义authentication_classes类,所以程序会从继承的类中去查找,视图类继承自APIView,所以在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 # 继承自APIView中的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
Summary
从上述的逻辑可以看出最终要执行的是AUTHENTICATION_CLASSES,所有的程序中都是如果有自定义程序会覆盖掉框架封装好的,没有自定义,程序才会执行封装好的代码。AUTHENTICATION_CLASSES类是这个逻辑中最重要的一环。上边的逻辑查找到了最基本的Authentication_classes,并且得到加工后的request包含两部分内容:原生的request、Authentication_classes实例化后得到的对象列表,此时需要继续执行dispatch(),执行到try语句时,加工后的request作为参数传递给initial()函数,并执行该函数,此时需要到request.py中查找initial()函数。
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被传递进行,原生的参数在调用的时候格式为:request._request, 加工后的直接是request.属性
class Request:
"""
Wrapper allowing to enhance a standardHttpRequest
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 # 加工后的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
如果进行认证,必须通过user,此时需要查找user程序是否存在,在Request类中找到了user函数,user()方法执行了_authenticate(),查找_authenticate()
@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() # 执行_authenticate() return self._user
查找_authenticate(),在_authenticate()方法中查找到Authenticator_classes生成的实例化列表类对象,循环的对象具有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: raise self._not_authenticated() if user_auth_tuple is not None: self._authenticator = authenticator self.user, self.auth = user_auth_tuple return self._not_authenticated()
二、自定义认证类
通过上述逻辑的整体分析,我们可以编写一个自定义的认证类供视图函数来调用
from rest_framework import exceptions # 创建自定义的认证类 class MyAuthentication(object): def authenticate(self, request): token = request._request.GET.get('token') # 当前传递的参数request是加工过的,需要通过原生的request(获取方法: _request)获取token信息 if not token: # 如果不存在抛出异常 raise exceptions.AuthenticationFailed("认证失败") # 存在返回元组 return ('alax', None) def authenticate(self, val): pass class DogView(APIView): ''' 需要认证的视图类直接通过authentication_classes=[类名,]的方式来使用 ''' authentication_classes = [MyAuthentication] def get(self, reqeust, *args, **kwargs): result = { 'code': '10000', 'msg': '获取到所有的数据' } return HttpResponse(json.dumps(result)) def post(self, request, *args, **kwargs): return HttpResponse("创建Dog") def put(self, request, *args, **kwargs): return HttpResponse("更新Dog") def delete(self, request, *args, **kwargs): return HttpResponse("删除Dog")