zoukankan      html  css  js  c++  java
  • rest_framework -- 认证组件

    #####认证组件#####
    
    一、认证是什么就不说了,某些网页必须是用户登陆之后,才能访问的,所以这时候就需要用上认证组件。
        你不用rest_framework的认证组件也行,这种认证的话,完全可以自己写出来。
    
    
    二、之前再写APIView的时候,那里提到过。
        不记得在哪里的话,先找dispatch方法(APIView的记得),然后是self.initial(request, *args, **kwargs),
        最后找到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是rest_framework进行封装后的request了,所以找到的话,就去rest_framework中Request去找。
            ## 下面附上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
            ## 它通过property将一个方法装饰成一个属性,此时self是request对象(rest_framework的),经过if判断,执行了self._authenticate()
            ## 那我们继续去看这个方法是什么,附上源码:
    
            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)
                    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()
            ### 此时的self也还是request对象(rest_framework的),self.authenticators这个是什么?
            # authenticators它是request的一个属性,那么我们在哪里生成了这个request对象呢?我们回到APIView的dispatch方法,找到这行代码
            # request = self.initialize_request(request, *args, **kwargs),有没有印象,得到一个rest_framework的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
                )
            authenticators=self.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]
            返回的是一个列表,那么self.authentication_classes应该就是列表(元组),此时self是视图类的对象
    
        ####重点:面向对象属性的查找顺序,记住!!
            方式一:我们可以在当前视图类中写一个authentication_classes的列表(元组),里面装着一个一个的类,
                   而这个类不是随便的一个类,是进行认证验证的类。
            方式二:当前视图类中没有authentication_classes这个属性,那么便会去APIView中去找该属性,肯定能APIView中能够找到该属性
                   authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES -->> api_settings它是一个对象
                   我们去看产生api_settings对象的类,其他的不说了,说说这段代码:
    
                        @property
                        def user_settings(self):
                            if not hasattr(self, '_user_settings'):
                                self._user_settings = getattr(settings, 'REST_FRAMEWORK', {})
                            return self._user_settings
                        这里的setting是通过from django.core import settings 导入的
                   大概意思是:如果django的settings文件中有'REST_FRAMEWORK',那么便会去那里找DEFAULT_AUTHENTICATION_CLASSES这个属性,
                              没有的话,便会去rest_framework的settings文件中找DEFAULT_AUTHENTICATION_CLASSES,
                   所以方式二可以这样写,在django的settings文件中写上这样的代码
                   REST_FRAMEWORK = {
                       'DEFAULT_AUTHENTICATION_CLASSES':[进行认证的类1,进行认证的类2],
    
                        }
            方式三:什么都不写,用rest_framework的settings文件中的DEFAULT_AUTHENTICATION_CLASSES
    
            好了,我们再回到self._authenticate()的源码来看,for循环一个装着对象的列表,所以authenticator就是一个对象,
            user_auth_tuple = authenticator.authenticate(self) --->>> 执行该对象的方法,将返回值赋给user_auth_tuple,
            我们使用前面的方式一,方式二,自己写认证类的的话,那么必须要有authenticate这个方法对吧,这个先放着,
            我们先看方式三,我猜rest_framework的settings文件中的DEFAULT_AUTHENTICATION_CLASSES里的认证类中,也肯定有authenticate方法,
            看看它是怎么写,我们跟着写不就好了嘛?
            地址:from rest_framework import authentication
            看了下每个类中都有authenticate,传来两个参数,一个self,一个request,那我们自己写的认证类也这样写。该方法的返回值将会赋值给user_auth_tuple,
            继续回到def _authenticate(self)这个方法中,继续看,如果返回值user_auth_tuple为None的话,将会继续for循环,返回值为True的话,
            那么这个返回值必须为一个元组,而且只能有两个元素。执行for循环的过程中,authenticate这个方法没有异常的话,那么表示验证成功。
    
    
        总结:上面把认证的整个流程都写了一般,那么需要些的东西我列出来,
              1、根据需求要求自己写一个认证类,该类必须要有authenticate这个方法,继承BaseAuthentication这个类
              2、验证通过的话,返回None或者两个元素的元组(列表也行)
              3、验证不通过的话,抛异常,抛这个异常exceptions.APIException
              4、假如只想当前视图类中使用认证功能的话,那么在当前视图类中添加authentication_classes属性
              5、想全局都想进行认证功能,就在django的settings文件中添加
                 REST_FRAMEWORK = {
                           'DEFAULT_AUTHENTICATION_CLASSES':[进行认证的类1,进行认证的类2],
                            }
              6、如果你既想全局配置,但是某个局部又不配置认证的话,那么就是该视图类中写authentication_classes,值为[],就好了。
    
    下面写个登陆验证的例子把:test页面必须登陆之后才能访问
    
    models文件:
    class User(models.Model):
        nid = models.AutoField(primary_key=True)
        name = models.CharField(max_length=32)
        password = models.CharField(max_length=32)
    
    
    class Token(models.Model):
        nid = models.AutoField(primary_key=True)
        token = models.UUIDField(max_length=64)
        user = models.OneToOneField(to='User')
    
    
    urls文件:
        url(r'login/',views.Login.as_view()),
        url(r'test/',views.Test.as_view()),
    
    
    views文件:
    class Login(APIView):
        def post(self, request, *args, **kwargs):
            response = {'status': 100, 'msg': '登陆成功'}
            name = request.data.get('name')
            password = request.data.get('password')
            try:
                user = models.User.objects.get(name=name, password=password)
                token = uuid.uuid4()
                ret = models.Token.objects.filter(user=user)
                if ret:
                    models.Token.objects.filter(user=user).update(token=token)
                else:
                    models.Token.objects.create(token=token, user=user)
                response['token'] = token
            except Exception:
                response['status'] = 101
                response['msg'] = '用户名或密码错误'
            return JsonResponse(response)
    
    class Test(APIView):
        authentication_classes = [LoginAuth,]
        def get(self, request, *args, **kwargs):
            return HttpResponse('test get')
    
    
    auth_class.py文件:
    
    class LoginAuth(BaseAuthentication):
        def authenticate(self, request):
            token = request.query_params.get('token', None)
            try:
                ret = models.Token.objects.get(token=token)
            except ObjectDoesNotExist:
                raise exceptions.APIException('请先进行登陆')
            return ret.user, ret
    
    
    这里推荐一个发送各种请求的软件,postman
  • 相关阅读:
    希尔伯特空间
    Java基础之类型转换总结篇
    超实用在线编译网站,编辑器
    3269: 万水千山粽是情
    Problem A: 李白打酒
    2370: 圆周率
    C语言fmod()函数:对浮点数取模(求余)
    C语言exp()函数:e的次幂函数(以e为底的x次方值)
    2543: 数字整除
    2542: 弟弟的作业
  • 原文地址:https://www.cnblogs.com/zhuchunyu/p/10133900.html
Copyright © 2011-2022 走看看