赞
踩
在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)
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
在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()方法几乎一样,但在启动、完成和异常处理等阶段有一些额外的钩子。
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认证了。
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()进行认证。
- 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方法
- 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')
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
自定义一个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
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
并在settings.py中设定REST_FRAMEWORK配置项
- REST_FRAMEWORK = {
- "DEFAULT_AUTHENTICATION_CLASSES": ['api.utils.auth.Authtication', ]
- }
'运行
此时所有继承自APIView的类都会继承authtication方法,如果有某个类不需要继承,可以在当前类中增加authentication_classes = []
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。