Django restframework 认证

一、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 = []

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值