一、APIView类
在as_view()一文中,我们自定义的视图类继承自View类,但在restframework中有一个APIView类,它继承并丰富了View类。
class APIView(View):
在APIView类中,有自己的as_view()和dispatch()方法,
@classmethod
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)
在as_view()方法中,又简单地调用了父类的as_view()方法,最后在返回的时候又调用了csrf_exempt()方法,除了基于session的认证都免于CSRF。
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
在dispatch()方法中,与Django原生的dispatch()方法几乎一样,但在启动、完成和异常处理等阶段有一些额外的钩子。
1、封装request
request = self.initialize_request(request, *args, **kwargs)
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
)
通过调用initialize_request()方法对原生的request进行封装,除了原生的request,还有authenticators,而authenticators是通过get_authenticators()方法返回的。
authenticators=self.get_authenticators(),
def get_authenticators(self):
"""
Instantiates and returns the list of authenticators that this view can use.
"""
return [auth() for auth in self.authentication_classes]
authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
在get_authenticators()方法中,是一个列表生成式,其中的authentication_classes是在api_settings中设定的,返回的列表中包含的是authentication对象
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_simplejwt.authentication.JWTAuthentication',
)
}
通过在settings.py中设定DEFAULT_AUTHENTICATION_CLASSES,就可以自定义全局的authentication认证了。
2、认证
self.initial(request, *args, **kwargs)
def initial(self, request, *args, **kwargs):
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)
调用了initial()方法,在该方法中,先是对请求的信息进行存储,对API的版本进行校验,最后调用了perform_authentication()进行认证。
3、实现认证
def perform_authentication(self, request):
request.user
在perform_authentication()方法中又调用了request的user()方法。
注意,这个request不是原生的request。
@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方法
4、获取认证对象
def _authenticate(self):
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()
循环所有的authentication对象,并执行对象的authenticate方法。如果我们想在当前类使用身份校验,需在方法中创建authenticate函数,
class ForcedAuthentication:
def __init__(self, force_user, force_token):
self.force_user = force_user
self.force_token = force_token
def authenticate(self, request):
return (self.force_user, self.force_token)
在authenticate方法中返回了一个元组,元组中包含一个user和一个token参数,所以在_authenticate函数的变量user_auth_tuple是一个元组。而在后面_authenticate方法中又添加了一个判断user_auth_tuple是否为空,如果不为空则返回出去,用我们自定义的值,反之调用了self._not_authenticated()方法。
def _not_authenticated(self):
self._authenticator = 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
在_not_authenticated方法中,会对api_settings中的UNAUTHENTICATED_USER和UNAUTHENTICATED_TOKEN进行判断,这两个都是在settings中设定的全局参数,如果全局参数没有设定,那么就赋值为None。
@user.setter
def user(self, value):
self._user = value
self._request.user = value
@auth.setter
def auth(self, value):
self._auth = value
self._request.auth = value
可以看到user和auth都是方法,不过添加了装饰器@user.setter和@auth.setter。在这两个方法中,分别将value赋给了APIView的request和原生的request。
二、实现自定义认证
通过前面的学习,我们已经了解了authentication的内部原理,接下来我们就可以实现自定义的authentication。
urlpatterns = [
path('DemoView/', views.DemoView.as_view()),
]
class Authtication(object):
def authenticate(self, request):
token = request._request.GET.get('token')
token_obj = None#models.UserToken.objects.filter(token=token).first()
if not token_obj:
raise exceptions.AuthenticationFailed('用户认证失败')
print(token_obj.user, token_obj)
return (token_obj.user, token_obj)
def authenticate_header(self, request):
pass
class DemoView(APIView):
authentication_classes = [Authtication, ]
def get(self, request):
return HttpResponse('get')
def post(self, request):
return HttpResponse('post')
自定义一个Authtication类,类中必须要有authenticate方法和authenticate_header方法,其中authenticate_header方法是认证失败时返回的响应头。
如果需要全局启用自定义的Authtication类,则需要在settings.py中设置全局调用类。
api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS)
api_settings是读取REST_FRAMEWORK的配置的。同时为了以后维护方便,我们可以将自定义的Authtication类写在utils/auth.py中。
from rest_framework import exceptions
from api import models
class Authtication(object):
def authenticate(self, request):
token = request._request.GET.get('token')
token_obj = models.UserToken.objects.filter(token=token).first()
if not token_obj:
raise exceptions.AuthenticationFailed('用户认证失败')
return (token_obj.user, token_obj)
def authenticate_header(self, request):
pass
并在settings.py中设定REST_FRAMEWORK配置项
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": ['api.utils.auth.Authtication', ]
}
此时所有继承自APIView的类都会继承authtication方法,如果有某个类不需要继承,可以在当前类中增加authentication_classes = []