前言
對於一門框架,掌握了它的基本使用後,如果能從源碼的角度理解它的一些常用功能的實現,不僅可以加深我們對web開發的理解,還能鞏固好我們的基礎知識
源碼分析
源碼大致流程分析
記住請求到view裏邊,如果是CBV的話,會先到類的dispatch裏邊,然後dispatch根據請求類型找到GET(),POST()等方法然後執行。
先來看看Django的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進行了封裝,
# 待會我們看看initialize_request做了啥
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
1)對原生的request進行封裝
initialize_request的源碼
下圖可看到initialize_request
加了四個東西,request不再是原來的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
)
get_authenticators(身份驗證器源碼)
def get_authenticators(self):
"""
Instantiates and returns the list of authenticators that this view can use.
"""
"""
由源碼可見這是一個列表推導式,此方法返回了一個auth對象的列表
"""
return [auth() for auth in self.authentication_classes]
接下來我們看看authentication_classes
裏面是啥
可見這是默認的,如果我們自己定義了,那麼就會用我們的,因爲它是從我們寫的類裏面找,找不到再去父類APIview中找
然後我們點api_settings
進去看看是啥玩意
api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS)
接下來點擊它繼承的defaults類看看是什麼
這裏的源碼寫了一大堆,寫了很多默認的類,我們主要看它那兩個默認認證的類
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication'
],
走到這裏的時候,pycharm沒法點往下走了,我們導入這兩個類看看
from rest_framework.authentication import SessionAuthentication
from rest_framework.authentication import BaseAuthentication
查看BaseAuthentication
源碼
class BaseAuthentication(object):
"""
All authentication classes should extend BaseAuthentication.
"""
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
從源碼中也可以看到authenticate方法必須被重寫,否則會拋異常
class BaseAuthentication
下面還有個BasicAuthentication
,這是做具體認證的流程,從headers裏面能獲取用戶名和密碼
class BasicAuthentication(BaseAuthentication):
"""
HTTP Basic authentication against username/password.
"""
www_authenticate_realm = 'api'
def authenticate(self, request):
"""
Returns a `User` if a correct username and password have been supplied
using HTTP Basic authentication. Otherwise returns `None`.
"""
auth = get_authorization_header(request).split()
if not auth or auth[0].lower() != b'basic':
return None
if len(auth) == 1:
msg = _('Invalid basic header. No credentials provided.')
raise exceptions.AuthenticationFailed(msg)
elif len(auth) > 2:
msg = _('Invalid basic header. Credentials string should not contain spaces.')
raise exceptions.AuthenticationFailed(msg)
try:
auth_parts = base64.b64decode(auth[1]).decode(HTTP_HEADER_ENCODING).partition(':')
except (TypeError, UnicodeDecodeError, binascii.Error):
msg = _('Invalid basic header. Credentials not correctly base64 encoded.')
raise exceptions.AuthenticationFailed(msg)
userid, password = auth_parts[0], auth_parts[2]
return self.authenticate_credentials(userid, password, request)
def authenticate_credentials(self, userid, password, request=None):
"""
Authenticate the userid and password against username and password
with optional request for context.
"""
credentials = {
get_user_model().USERNAME_FIELD: userid,
'password': password
}
user = authenticate(request=request, **credentials)
if user is None:
raise exceptions.AuthenticationFailed(_('Invalid username/password.'))
if not user.is_active:
raise exceptions.AuthenticationFailed(_('User inactive or deleted.'))
return (user, None)
def authenticate_header(self, request):
return 'Basic realm="%s"' % self.www_authenticate_realm
當然restfulframework默認定義了兩個類。我們也可以自定製類,自己有就用自己的了,自己沒有就去找父類的了,但是裏面必須實現authenticate方法,不然會報錯。
2)認證流程
首先看self.initial(request, *args, **kwargs)
源碼
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)
可以看到做認證是通過self.perform_authentication(request)
,點進去看看
def perform_authentication(self, request):
"""
Perform authentication on the incoming request.
Note that if you override this and simply 'pass', then authentication
will instead be performed lazily, the first time either
`request.user` or `request.auth` is accessed.
"""
request.user
它返回了一個request.user,注意這個request已經不是原生的request,他是封裝過的,
導入Request對象,找到它的user方法
@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
可以看到user()被設置爲靜態屬性,所以上面沒有加括號就直接調用了,點擊去看看_authenticate
def _authenticate(self):
"""
Attempt to authenticate the request using each authentication instance
in turn.
"""
# 循環認證類的所有對象
for authenticator in self.authenticators:
try:
"""
執行認證類的authenticate方法,
1.如果authenticate方法拋出異常,執行_not_authenticated()
2.有返回值,必須是元組:(request.user,request.auth)
把元組的第一個元素賦值給self.user(用戶),
第二個元素賦值給self.auth(用戶的token)
3.返回None,就不管,下一個認證來處理(raise是向上拋出異常)
"""
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()
**然後我們在來看看_not_authenticated()是幹啥的,**認證失敗會幹啥,
def _not_authenticated(self):
"""
Set authenticator, user & authtoken representing an unauthenticated request.
Defaults are None, AnonymousUser & None.
"""
#如果跳過了所有認證,默認用戶和Token和使用配置文件進行設置
self._authenticator = None #
if api_settings.UNAUTHENTICATED_USER:
self.user = api_settings.UNAUTHENTICATED_USER() # 默認值爲:匿名用戶AnonymousUser
else:
self.user = None # None 表示跳過該認證
if api_settings.UNAUTHENTICATED_TOKEN:
self.auth = api_settings.UNAUTHENTICATED_TOKEN() # 默認值爲:None
else:
self.auth = None
# (user, token)
# 表示驗證通過並設置用戶名和Token;
可見,如果上面都沒處理,我就再這加個默認值AnonymousUser和None
認證總的流程
- 第一步,請求到dispatch之後,原生的request被封裝,加上了四個東西,其中一個就是用戶認證的。
- 第二步:執行initial()方法裏面的
perform_authentication
- 第三步:執行
user
,user已經被聲明爲靜態屬性, - 第四部,執行
_authenticate()