1、认证函数源码分析
1.1 用途
在开发API过程中,有些功能需要登录才能访问,有些无需登录。drf中的认证组件主要就是用来实现此功能。
关于认证组件,我们用案例的形式,先来学习常见的用用场景,然后再来剖析源码。
项目要开发100个接口,其中1个无需登录接口、98个必须登录才能访问的接口、1个公共接口(未登录时显示公共/已登录时显示个人信息)。
原来的认证信息只能放在URL中传递,如果程序中支持放在很多地方,例如:URL中、请求头中等。
认证组件中,如果是使用了多个认证类,会按照顺序逐一执行其中的authenticate
方法
- 返回None或无返回值,表示继续执行后续的认证类
- 返回 (user, auth) 元组,则不再继续并将值赋值给request.user和request.auth
- 抛出异常
AuthenticationFailed(...)
,认证失败,不再继续向后走
1.2 分析
源码链路:
1、认证函数执行步骤
称 APIView 为 A Request为R,因为自定义的视图类,基本上实例化方法都继承A所以,后面都以 A代替 实例化视图类为obj1, 实例化Request类为r1
ob1.dispatch() >> 代码入口,实例化视图类调用
obj1. initialize_request >> 把认证类列表 封装到request类,实例化视图类调用
obj1.get_authenticators >> 返回实例化对象的认证类列表,实例化视图类调用
obj1.initial >> 实例化视图类调用
obj1.perform_authentication >> 实例化视图类调用
r1.user >> 实例化Request调用
r1._authenticate >> 实例化Request调用
authenticate(r1) 实例化Request当做参数传入自定义的 authenticate 函数
源码分析
# 在视图类中,如果我们重写authentication_classes,则以重写为准,本质就是继承父类重写了类属性,如果不重写authentication_classes,那么就继承APIView 中的 authentication_classes 属性,而authentication_classes默认是写在api_settings配置中,所以如果要全局使用相同的认证组件,可以直接在配置文件中配置就可以
class OrderView(APIView):
authentication_classes = [MyAuthentication, ]
def get(self, request):
pass
# 源码分析,(下面代码不讲究python格式,纯粹是笔记)
class APIView(View):
# 默认读取配置文件中 DEFAULT_AUTHENTICATION_CLASSES 认证类
# 一般配置的时候是以一个列表进行配置
authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
#认证类组件也是从dispatch 为入口,只摘取部分关键代码,简化代码
def dispatch(self, request, *args, **kwargs):
self.args = args
self.kwargs = kwargs
request = self.initialize_request(request, *args, **kwargs)
######### start: initialize_request 源码 ####################
def initialize_request(self, request, *args, **kwargs):
parser_context = self.get_parser_context(request)
return Request(
request,
parsers=self.get_parsers(),
# authenticators是对应各个认证类的实例化
authenticators=self.get_authenticators(),
######### start: get_authenticators 源码 ####################
def get_authenticators(self):
#返回一个实例化的认证类列表
return [auth() for auth in self.authentication_classes]
######### end: get_authenticators 源码 ####################
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)
######### end: initialize_request 源码 ####################
self.request = request
self.headers = self.default_response_headers # deprecate?
try:
self.initial(request, *args, **kwargs)
######### start: initial 源码 ####################
def initial(self, request, *args, **kwargs):
'''省略部分代码'''
self.perform_authentication(request)
######### start: perform_authentication 源码#########
def perform_authentication(self, request):
request.user
######### start: request.user 源码##########
@property
def user(self):
if not hasattr(self, '_user'):
with wrap_attributeerrors():
self._authenticate()
######### start: _authenticate 源码##########
def _authenticate(self):
# 遍历每个认证类
for authenticator in self.authenticators:
try:
# 对drf的request进行认证
user_auth_tuple =
# ****画重点**** 下面的self就是Request实例化的类
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()
######### end: _authenticate 源码##########
return self._user
######### end: request.user 源码##########
######### end: perform_authentication 源码##########
self.check_permissions(request)
self.check_throttles(request)
######### end: initial 源码 ####################
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
认证类的使用
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from api import models
# 类BaseAuthentication源码
class BaseAuthentication:
"""
All authentication classes should extend BaseAuthentication.
"""
# NotImplementedError 表示必须被重新定义,否则会调用authenticate然后报错
def authenticate(self, request):
"""
Authenticate the request and return a two-tuple of (user, token).
"""
raise NotImplementedError(".authenticate() must be overridden.")
# 可以被重新定义,也可以不被重新定义
def authenticate_header(self, request):
"""
Return a string to be used as the value of the `WWW-Authenticate`
header in a `401 Unauthenticated` response, or `None` if the
authentication scheme should return `403 Permission Denied` responses.
"""
pass
#### 认证Request下源码
def _authenticate(self):
"""
Attempt to authenticate the request using each authentication instance
in turn.
"""
# authenticator是自定义的 authenticator 类
for authenticator in self.authenticators:
try:
# 使用自定义的authenticate 函数认证request
# user_auth_tuplef返回的结果,如果报错,错误將会被抛出
##
## ***** 画重点****,这个地方self本身指的就是 Request实例
## ***所以就传了request进去了***
user_auth_tuple = authenticator.authenticate(self)
except exceptions.APIException:
self._not_authenticated()
raise
# user_auth_tuple如果是None则表示认证没有通过,然后下一个
# 认证函数进行认证,有一认证函数通过则表示认证通过,有一个
# 认证函数出错,则就报错
# 认证通过后 user_auth_tuple 通常是一个元组的形式赋值给self.user,
# self.auth
if user_auth_tuple is not None:
self._authenticator = authenticator
self.user, self.auth = user_auth_tuple
return
self._not_authenticated()
def _not_authenticated(self):
self._authenticator = None
# 將匿名用户user设置为None
if api_settings.UNAUTHENTICATED_USER:
self.user = api_settings.UNAUTHENTICATED_USER()
else:
self.user = None
if api_settings.UNAUTHENTICATED_TOKEN:
self.auth = api_settings.UNAUTHENTICATED_TOKEN()
else:
self.auth = None
# 使用认证组件
class QueryParamsAuthentication(BaseAuthentication):
def authenticate(self, request):
token = request.query_params.get("token")
if not token:
# 什么都没有返回,相当于返回None
return
user_object = models.UserInfo.objects.filter(token=token).first()
if user_object:
return user_object, token # request.user = 用户对象; request.auth = token
# 这个先不管,不重要
def authenticate_header(self, request):
# return 'Basic realm="API"'
return "API"
class HeaderAuthentication(BaseAuthentication):
def authenticate(self, request):
token = request.META.get("HTTP_AUTHORIZATION")
if not token:
return
user_object = models.UserInfo.objects.filter(token=token).first()
if user_object:
return user_object, token # request.user = 用户对象; request.auth = token
return
def authenticate_header(self, request):
# return 'Basic realm="API"'
return "API"
# 当前面认证都没有通过时,可以直接自定义authenticate,抛出错误
class NoAuthentication(BaseAuthentication):
def authenticate(self, request):
raise AuthenticationFailed({"status": False, 'msg': "认证失败"})
def authenticate_header(self, request):
return "API"