认证模块
rest_framework.authentication
是restframework提供的认证模块,其他认证类都要继承BaseAuthentication
类。
分析认证源码流程
from rest_framework.views import APIView
class IndexView(APIView):
def get(self,request):
return HttpResponse("success")
APIView
继承自django的View
(from django.views import View)
根据CBV流程,最终会调用self.dispatch(request, *args, **kwargs)
,self指向IndexView示例,先去IndexView查找dispatch(),没有再去APIView查找,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对象
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对象
把原来的request作为参数传递给了initialize_request(...)
request = self.initialize_request(request, *args, **kwargs)
self.request = request
self指向IndexView实例,先去IndexView类查找,未找到再去父类查找,在父类找到initialize_request(...)方法,源码如下:
class APIView(View):
...
authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES#如果在django项目settings.py中定义了`REST_FRAMEWORK`变量,会优先从那里读取
...
def get_authenticators(self):
"""
Instantiates and returns the list of authenticators that this view can use.
"""
return [auth() for auth in self.authentication_classes] #这里self指向IndexView实例,先去IndexView中查找,如果未定义再去父类查找
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
)
调用了Request类,重新封装了原来的request,并且增加了authenticators
属性,本篇只看认证,其他先忽略。
本例在settings.py中定义了认证类:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication',
],
}
所以authenticators = [BasicAuthentication(),SessionAuthentication()]
如何从新的Request对象中获取请求参数
Request类定义:from rest_framework.request import Request
class IndexView(APIView):
def get(self,request):
print(request._request.GET.get("user")) #直接使用原生的request对象
print(request.query_params.get("user"))
print(request.GET.get("user"))
return HttpResponse("success")
request.query_params.get("user")
查看Request类中的定义:
@property
def query_params(self):
"""
More semantically correct name for request.GET.
"""
return self._request.GET
print(request.GET.get("user"))
为什么封装后的request对象也能直接调用GET属性获取,因为重写了内置方法__getattr__()
:
def __getattr__(self, attr):
"""
If an attribute does not exist on this instance, then we also attempt
to proxy it to the underlying HttpRequest object.
"""
try:
return getattr(self._request, attr)
except AttributeError:
return self.__getattribute__(attr)
验证是否认证
self.initial(request, *args, **kwargs)
这里的request是封装后的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属性的实现代码:
@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
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)#authenticate()可以有三种结果1、返回元祖包含两个元素作为self.user和self.auth赋值 2、返回None 3、可以抛异常
except exceptions.APIException:
self._not_authenticated()
raise
if user_auth_tuple is not None:#遍历self.authenticators,如果返回None就去遍历下一个对象,如果得到tuple赋值后就返回
self._authenticator = authenticator
self.user, self.auth = user_auth_tuple
return
self._not_authenticated()
def _not_authenticated(self):#匿名用户self.user和self.auth赋值
"""
Set authenticator, user & authtoken representing an unauthenticated request.
Defaults are None, AnonymousUser & None.
"""
self._authenticator = None
if api_settings.UNAUTHENTICATED_USER:#可以在settings.py定义UNAUTHENTICATED_USER变量,指向一个函数或者直接为None
self.user = api_settings.UNAUTHENTICATED_USER()
else:
self.user = None
if api_settings.UNAUTHENTICATED_TOKEN:#可以在settings.py定义UNAUTHENTICATED_TOKEN变量,指向一个函数或者直接为None
self.auth = api_settings.UNAUTHENTICATED_TOKEN()
else:
self.auth = None
for authenticator in self.authenticators:
这里的self.authenticators
就是 刚才分析得到的[BasicAuthentication(),SessionAuthentication()]
遍历list中对象,调用authenticator.authenticate(self)
进行认证,这里self指的是封装的Request实例对象
以BasicAuthentication
为例,查看源码:
from rest_framework.authentication import BasicAuthentication,SessionAuthentication
class BaseAuthentication:
"""
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
class BasicAuthentication(BaseAuthentication):
"""
HTTP Basic authentication against username/password.
"""
www_authenticate_realm = 'api'
def authenticate(self, request):...
def authenticate_credentials(self, userid, password, request=None):...
def authenticate_header(self, request):
return 'Basic realm="%s"' % self.www_authenticate_realm
BaseAuthentication
作为内置认证类的父类,提供了authenticate()
和authenticate_header()
,子类必须实现authenticate()
方法
自定义认证类
根据验证认证的规则,自定义认证类,继承自BaseAuthentication
utils目录下新建auth.py
from rest_framework import exceptions
from rest_framework.authentication import BaseAuthentication
class MyAuthentication(BaseAuthentication):
def authenticate(self, request):
if request._request.GET.get("user"):
return ('xiaobai','test token')
else:
raise exceptions.AuthenticationFailed("用户认证失败")
def authenticate_header(self, request):
pass
views.py
from django.http import HttpResponse
from rest_framework.views import APIView
from utils.auth import MyAuthentication
class IndexView(APIView):
authentication_classes = [MyAuthentication]
def get(self,request):
print(request.user)
print(request.auth)
return HttpResponse("success")
>>>xiaobai
>>>test token
全局配置
如果想让所有视图都必须要认证后才能访问,可以在django项目的settings.py中设置REST_FRAMEWORK
变量,即本篇开始时写的,修改成自定义的认证类:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': ['utils.auth.MyAuthentication'],
'UNAUTHENTICATED_USER':None,#_not_authenticated()函数内部调用,如果没有覆盖使用rest_framework settings里面默认的。覆盖后结果request.user = None
'UNAUTHENTICATED_TOKEN':None#_not_authenticated()函数内部调用,如果没有覆盖使用rest_framework settings里面默认的。覆盖后结果request.auth = None
}
局部配置
如果只是某个视图使用,可以在类中定义属性,比如:authentication_classes = [MyAuthentication]
。
如果不需要认证,可以写成authentication_classes = []
网友评论