当前位置:   article > 正文

Django restframework 认证

Django restframework 认证

一、APIView类

在​​as_view()一文中,我们自定义的视图类继承自View类,但在restframework中有一个APIView类,它继承并丰富了View类。

class APIView(View):

在APIView类中,有自己的as_view()和dispatch()方法,

  1. @classmethod
  2. def as_view(cls, **initkwargs):
  3. """
  4. Store the original class on the view function.
  5. This allows us to discover information about the view when we do URL
  6. reverse lookups. Used for breadcrumb generation.
  7. """
  8. if isinstance(getattr(cls, 'queryset', None), models.query.QuerySet):
  9. def force_evaluation():
  10. raise RuntimeError(
  11. 'Do not evaluate the `.queryset` attribute directly, '
  12. 'as the result will be cached and reused between requests. '
  13. 'Use `.all()` or call `.get_queryset()` instead.'
  14. )
  15. cls.queryset._fetch_all = force_evaluation
  16. view = super().as_view(**initkwargs)
  17. view.cls = cls
  18. view.initkwargs = initkwargs
  19. # Note: session based authentication is explicitly CSRF validated,
  20. # all other authentication is CSRF exempt.
  21. return csrf_exempt(view)

在as_view()方法中,又简单地调用了父类的as_view()方法,最后在返回的时候又调用了csrf_exempt()方法,除了基于session的认证都免于CSRF

  1. def dispatch(self, request, *args, **kwargs):
  2. """
  3. `.dispatch()` is pretty much the same as Django's regular dispatch,
  4. but with extra hooks for startup, finalize, and exception handling.
  5. """
  6. self.args = args
  7. self.kwargs = kwargs
  8. request = self.initialize_request(request, *args, **kwargs)
  9. self.request = request
  10. self.headers = self.default_response_headers # deprecate?
  11. try:
  12. self.initial(request, *args, **kwargs)
  13. # Get the appropriate handler method
  14. if request.method.lower() in self.http_method_names:
  15. handler = getattr(self, request.method.lower(),
  16. self.http_method_not_allowed)
  17. else:
  18. handler = self.http_method_not_allowed
  19. response = handler(request, *args, **kwargs)
  20. except Exception as exc:
  21. response = self.handle_exception(exc)
  22. self.response = self.finalize_response(request, response, *args, **kwargs)
  23. return self.response
'
运行

在dispatch()方法中,与Django原生的dispatch()方法几乎一样,但在启动、完成和异常处理等阶段有一些额外的钩子。

1、封装request

request = self.initialize_request(request, *args, **kwargs)
  1. def initialize_request(self, request, *args, **kwargs):
  2. """
  3. Returns the initial request object.
  4. """
  5. parser_context = self.get_parser_context(request)
  6. return Request(
  7. request,
  8. parsers=self.get_parsers(),
  9. authenticators=self.get_authenticators(),
  10. negotiator=self.get_content_negotiator(),
  11. parser_context=parser_context
  12. )
'
运行

通过调用initialize_request()方法对原生的request进行封装,除了原生的request,还有authenticators,而authenticators是通过get_authenticators()方法返回的。

authenticators=self.get_authenticators(),
  1. def get_authenticators(self):
  2. """
  3. Instantiates and returns the list of authenticators that this view can use.
  4. """
  5. return [auth() for auth in self.authentication_classes]
'
运行
authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES

在get_authenticators()方法中,是一个列表生成式,其中的authentication_classes是在api_settings中设定的,返回的列表中包含的是authentication对象

  1. REST_FRAMEWORK = {
  2. 'DEFAULT_AUTHENTICATION_CLASSES': (
  3. 'rest_framework_simplejwt.authentication.JWTAuthentication',
  4. )
  5. }
'
运行

通过在settings.py中设定DEFAULT_AUTHENTICATION_CLASSES,就可以自定义全局的authentication认证了。

2、认证

self.initial(request, *args, **kwargs)
  1. def initial(self, request, *args, **kwargs):
  2. self.format_kwarg = self.get_format_suffix(**kwargs)
  3. # Perform content negotiation and store the accepted info on the request
  4. neg = self.perform_content_negotiation(request)
  5. request.accepted_renderer, request.accepted_media_type = neg
  6. # Determine the API version, if versioning is in use.
  7. version, scheme = self.determine_version(request, *args, **kwargs)
  8. request.version, request.versioning_scheme = version, scheme
  9. # Ensure that the incoming request is permitted
  10. self.perform_authentication(request)
  11. self.check_permissions(request)
  12. self.check_throttles(request)
'
运行

调用了initial()方法,在该方法中,先是对请求的信息进行存储,对API的版本进行校验,最后调用了perform_authentication()进行认证。

3、实现认证

  1. def perform_authentication(self, request):
  2. request.user
'
运行

在perform_authentication()方法中又调用了request的user()方法。

注意,这个request不是原生的request。

  1. @property
  2. def user(self):
  3. """
  4. Returns the user associated with the current request, as authenticated
  5. by the authentication classes provided to the request.
  6. """
  7. if not hasattr(self, '_user'):
  8. with wrap_attributeerrors():
  9. self._authenticate()
  10. return self._user

在user方法中,又调用了_authenticate方法

4、获取认证对象

  1. def _authenticate(self):
  2. for authenticator in self.authenticators:
  3. try:
  4. user_auth_tuple = authenticator.authenticate(self)
  5. except exceptions.APIException:
  6. self._not_authenticated()
  7. raise
  8. if user_auth_tuple is not None:
  9. self._authenticator = authenticator
  10. self.user, self.auth = user_auth_tuple
  11. return
  12. self._not_authenticated()
'
运行

循环所有的authentication对象,并执行对象的authenticate方法。如果我们想在当前类使用身份校验,需在方法中创建authenticate函数,

  1. class ForcedAuthentication:
  2. def __init__(self, force_user, force_token):
  3. self.force_user = force_user
  4. self.force_token = force_token
  5. def authenticate(self, request):
  6. return (self.force_user, self.force_token)
'
运行

在authenticate方法中返回了一个元组,元组中包含一个user和一个token参数,所以在_authenticate函数的变量user_auth_tuple是一个元组。而在后面_authenticate方法中又添加了一个判断user_auth_tuple是否为空,如果不为空则返回出去,用我们自定义的值,反之调用了self._not_authenticated()方法。

  1. def _not_authenticated(self):
  2. self._authenticator = None
  3. if api_settings.UNAUTHENTICATED_USER:
  4. self.user = api_settings.UNAUTHENTICATED_USER()
  5. else:
  6. self.user = None
  7. if api_settings.UNAUTHENTICATED_TOKEN:
  8. self.auth = api_settings.UNAUTHENTICATED_TOKEN()
  9. else:
  10. self.auth = None
'
运行

在_not_authenticated方法中,会对api_settings中的UNAUTHENTICATED_USER和UNAUTHENTICATED_TOKEN进行判断,这两个都是在settings中设定的全局参数,如果全局参数没有设定,那么就赋值为None。

  1. @user.setter
  2. def user(self, value):
  3. self._user = value
  4. self._request.user = value
  5. @auth.setter
  6. def auth(self, value):
  7. self._auth = value
  8. self._request.auth = value

 可以看到user和auth都是方法,不过添加了装饰器@user.setter和@auth.setter。在这两个方法中,分别将value赋给了APIView的request和原生的request。

二、实现自定义认证

通过前面的学习,我们已经了解了authentication的内部原理,接下来我们就可以实现自定义的authentication。

  1. urlpatterns = [
  2. path('DemoView/', views.DemoView.as_view()),
  3. ]
  1. class Authtication(object):
  2. def authenticate(self, request):
  3. token = request._request.GET.get('token')
  4. token_obj = None#models.UserToken.objects.filter(token=token).first()
  5. if not token_obj:
  6. raise exceptions.AuthenticationFailed('用户认证失败')
  7. print(token_obj.user, token_obj)
  8. return (token_obj.user, token_obj)
  9. def authenticate_header(self, request):
  10. pass
  11. class DemoView(APIView):
  12. authentication_classes = [Authtication, ]
  13. def get(self, request):
  14. return HttpResponse('get')
  15. def post(self, request):
  16. 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中。

  1. from rest_framework import exceptions
  2. from api import models
  3. class Authtication(object):
  4. def authenticate(self, request):
  5. token = request._request.GET.get('token')
  6. token_obj = models.UserToken.objects.filter(token=token).first()
  7. if not token_obj:
  8. raise exceptions.AuthenticationFailed('用户认证失败')
  9. return (token_obj.user, token_obj)
  10. def authenticate_header(self, request):
  11. pass

并在settings.py中设定REST_FRAMEWORK配置项

  1. REST_FRAMEWORK = {
  2. "DEFAULT_AUTHENTICATION_CLASSES": ['api.utils.auth.Authtication', ]
  3. }
'
运行

此时所有继承自APIView的类都会继承authtication方法,如果有某个类不需要继承,可以在当前类中增加authentication_classes = []

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/一键难忘520/article/detail/876783
推荐阅读
相关标签
  

闽ICP备14008679号