本文介绍关于drf框架中的用户认证的源码流程
在drf框架中我们通过相关组件来实现用户认证,下面是导包的语句
javascript
from rest_framework.authentication import BaseAuthentication
这段代码我来判断用户的请求头中是否传入了token信息来进行用户验证,当然这里可以自行修需要验证的内容
kotlin
class MyAuthentication(BaseAuthentication):
def authenticate(self, request):
token=request.query_params.get("token")
print(token)
if not token:
return
user_object=models.Userinfo.objects.filter(token=token).first()
if user_object:
return user_object,token
return
这段代码来判断请求头中有没有token
kotlin
# 判断请求头是否有token
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
return
下面这段代码是我们判断如果没有通过验证,将会返回某些认证信息,这里需要导入AuthenticationFailed包
ruby
class NoAuthentication(BaseAuthentication):
def authenticate(self, request):
raise AuthenticationFailed({"code":10001,"msg":"认证失败"})
下面是思路梳理 一切的一切开始都从url开始,我们首先调用了as_view()方法,那么就仅需里面看看发生了什么
python
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().as_view(**initkwargs)
view.cls = cls
view.initkwargs = initkwargs
# Note: session based authentication is explicitly CSRF validated,
# all other authentication is CSRF exempt.
return csrf_exempt(view)
这里返回view,从17行我们可以知道他调用了父类的方法,进去查看一下
python
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(
"The method name %s is not accepted as a keyword argument "
"to %s()." % (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)
self.setup(request, *args, **kwargs)
if not hasattr(self, "request"):
raise AttributeError(
"%s instance has no 'request' attribute. Did you override "
"setup() and forget to call super()?" % cls.__name__
)
return self.dispatch(request, *args, **kwargs)
view.view_class = cls
view.view_initkwargs = initkwargs
# __name__ and __qualname__ are intentionally left unchanged as
# view_class should be used to robustly determine the name of the view
# instead.
view.__doc__ = cls.__doc__
view.__module__ = cls.__module__
view.__annotations__ = cls.dispatch.__annotations__
# Copy possible attributes set by decorators, e.g. @csrf_exempt, from
# the dispatch method.
view.__dict__.update(cls.dispatch.__dict__)
# Mark the callback if the view class is async.
if cls.view_is_async:
markcoroutinefunction(view)
return view
他返回的view值在第24行的dispatch里,这里我们来到他的apiview里面去查找dispatch方法 (注意这里的dispatch方法有很多都有,我们要看看是谁调用的这个方法,这里涉及面向对象的知识,不懂的可以去回顾一下)
python
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
我们发现前面的request封装被封装成了一个新的对象,不同于原来的django中的request,现在封装成新的request对象,继承了原来的方法,但是也增加了新的内容,这里感兴趣的可以自己看看,这里不再说明,接着他在第13行进行了一个定义方法,我们进去查看一下
python
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)
(这里的17,18,19行分别是认证组件,权限组件,节流组件) 进入17行我们来看一下
python
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的user是什么 我们进去,发现def user函数,这里有个装饰器,不懂得再去回顾回顾,主要是这样就能够直接调用这个函数,同理这里我们也找到了auth方法,所以我们能够直接调用request.auth和request.user
python
@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
python
@property
def auth(self):
"""
Returns any non-user authentication information associated with the
request, such as an authentication token.
"""
if not hasattr(self, '_auth'):
with wrap_attributeerrors():
self._authenticate()
return self._auth
self._authenticate()这个方法是什么呢,我们进去看看
python
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()
这里的authenticators是我们创建的各个认证类,他通过循环遍历,然后查找authenticate方法,这里要求返回user,和auth两个参数,所以也就是我们一开始需要两个返回值的原因,之后便可以通过调用request.auth和request.user来获取数据了