zoukankan      html  css  js  c++  java
  • Django之REST_FRAMEWORK 认证组件

    Django之DRF之认证组件

    # from rest_framework.views import APIView
    # APIView 中的  dispatch  中 执行的  self.initial(request,*args,**kwargs)中的 
    # APIView---->dispatch------>self.initial------>三个校验
    		# self.perform_authentication(request)  # 认证校验
    		# self.check_permissions(request)  # 权限校验
    		# self.check_throttles(request)  # 频率校验
    # 这里的self 指的是我写的那个序列化类
    
        def perform_authentication(self, request):
            # 这里的request 是新的request  原生的request 在   新request._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.
            """
            # 返回的user 是 新的request 中的一个 被伪装成属性的方法(@property)
            # 想要超看这个 需要导包  from rest_frmework.request import Request
            request.user
    
        def check_permissions(self, request):
            """
            Check if the request should be permitted.
            Raises an appropriate exception if the request is not permitted.
            """
            for permission in self.get_permissions():
                if not permission.has_permission(request, self):
                    self.permission_denied(
                        request, message=getattr(permission, 'message', None)
                    )
    

    这里的user方法属于 :

    from rest_framework.request import Request
    # Request类中--->user方法----->里面执行了  _authenticate(self) 方法
    

    是 被伪装成数据属性,

    @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 是 Request 对象
                    self._authenticate()  # 这里执行的是 Request 类中的_authenticate()
            return self._user
    

    重点的逻辑就是如下这段代码:

        # APIView-->dispatch--->self.initial--->self.perform_authentication(request)
        # --->reuqest.user---->self._authenticate
        def _authenticate(self):
            """
            Attempt to authenticate the request using each authentication instance
            in turn.
            尝试依次使用每个身份验证实例对请求进行身份验证。
            """
            # 这里的 self.autenticators 是一个元组,里面存的是一个个实例化的对象
            # 这里元组 是 Request 类在实例化的时候执行 __init__方法的时候赋值来的
            for authenticator in self.authenticators:
                try:
                    # 这里是执行自己写的认证类中的的 authenticate()方法,得到的是一个元组(这里面存的是 user对象  和 一个auth对象)!!!如果有返回值的话
                    user_auth_tuple = authenticator.authenticate(self)
                    # 如果该方法中返回 user对象 和 auth对象 
                except exceptions.APIException:
                    self._not_authenticated()  # 如果没有通过认证走这个方法
                    raise
    		   # 如果user_auth_tuple 有值,不为空
                if user_auth_tuple is not None:
                    self._authenticator = authenticator
                    self.user, self.auth = user_auth_tuple
                    return
    
            self._not_authenticated()
    

    认证组件的使用

    models.py

    from django.db import models
    
    
    # Create your models here.
    
    
    class Book(models.Model):
        title = models.CharField(max_length=32)
        price = models.DecimalField(max_digits=8, decimal_places=2)
        publish_time = models.DateTimeField(auto_now_add=True)  # 自动添加创建时间
        authors = models.ManyToManyField('Author')
        publish = models.ForeignKey('Publish')  # 一对多
        
        def test(self):
            return self.title+'>>'+str(self.price)
    
    
    class Author(models.Model):
        name = models.CharField(max_length=32)
        age = models.IntegerField()
        authordetail = models.OneToOneField('AuthorDetail')
    
    
    class AuthorDetail(models.Model):
        tel_num = models.BigIntegerField()
        addr = models.CharField(max_length=32)
    
    
    class Publish(models.Model):
        name = models.CharField(max_length=32)
        addr = models.CharField(max_length=32)
        email = models.EmailField()
    
    # 用户类
    class User(models.Model):
        name = models.CharField(max_length=32)
        pwd = models.CharField(max_length=32)
    # token类 与用户类一对一关系
    class Token(models.Model):
        user = models.OneToOneField(to='User')
        token = models.CharField(max_length=64)
    

    认证类的建立(一般新建一个py文件)

    from rest_framework.exceptions import AuthenticationFailed
    from rest_framework.authentication import BaseAuthentication
    from app01 import models
    
    class MyAuth(BaseAuthentication):
        # 在认证组件中  需要重写 authenticate方法来 完成 用户认证逻辑
        def authenticate(self, request):
            # request值得是个对象
            token = request.GET.get('token')
            token_obj = models.Token.objects.filter(token=token).first()
            if token_obj:
                # 有值表示登录了
                return
            else:
                # 没有值,则报错
                raise AuthenticationFailed('您没有登录')
    

    views.py

    from django.shortcuts import render
    from django.http.response import JsonResponse
    
    # Create your views here.
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from app01 import models
    from app01.myser import BookSerializer
    from app01 import auths
    from django.core.exceptions import ObjectDoesNotExist
    import uuid
    
    
    
    
    class Book(APIView):
        # 这里固定 需要些 这个认证类的列表,列表中存的是 一个一个认证的类
        authentication_classes = [auths.MyAuth,]
        # get 获取所有书籍信息
        def get(self, request, id):
            response = {'status': 100, 'msg': '成功'}
            print(id)
            if not id:
                book_list = models.Book.objects.all()
                # 第一个参数是要序列化的queryset对象,如果要序列化多条,必须制定many=True
                # 当instance形参被传入的实参是单个参数的时候,many=False
                book_serializer = BookSerializer(book_list, many=True)
            else:
                print(id)
                book_obj = models.Book.objects.filter(pk=id).first()
                book_serializer = BookSerializer(book_obj, many=False)
            print(book_serializer.data)
            response['books'] = book_serializer.data
            return Response(response)
        
        def post(self, request, id):
            response = {'status': 100, 'msg': '成功'}
            # 提交的字典
            book = request.data
            # 传统方法,创建对象保存
            print(book)
            
            # 新方法,通过序列化组件保存,必须继承自ModelSerializer
            book_ser = BookSerializer(data=book)
            # is_valid 提交的字段校验通过
            if book_ser.is_valid():
                book_ser.save()
                response['book'] = book_ser.data
            else:
                response['msg'] = book_ser.errors  # errors  是序列化类 中的钩子函数 raise来的报错信息
            return Response(response)
        
        def put(self, request, id):
            response = {'status': 100, 'msg': '修改成功'}
            if id:
                
                # 提交的字典
                book = request.data
                # 传统方法,创建对象保存
                print(book)
                book_obj = models.Book.objects.filter(pk=id).first()
                
                # 新方法,通过序列化组件保存,必须继承自ModelSerializer
                book_ser = BookSerializer(data=book, instance=book_obj)
                # is_valid 提交的字段校验通过
                if book_ser.is_valid():
                    # 这里save()做修改
                    book_ser.save()
                    response['book'] = book_ser.data
                else:
                    response['msg'] = book_ser.errors
            else:
                response['msg'] = '修改对象不存在'
            return Response(response)
        
        def delete(self, request, id):
            models.Book.objects.filter(pk=id).delete()
            response = {'status': 100, 'msg': '删除成功'}
            return Response(response)
    
    class Login(APIView):
        # 局部禁用 认证
        authentication_classes = []
        def post(self, request):
            response = {'code': 100, 'msg': '登录成功'}
            name = request.data.get('name')
            pwd = request.data.get('pwd')
            print(name, pwd)
            try:
                # get()方法,获取 有且只有一条的 才不报错,其他情况都抛异常
                ret = models.User.objects.filter(name=name, pwd=pwd).get()
                
                # 登录成功后要去token 表中去存数据
                # 表里有 数据或没有数据
                # 1. 先生成随机字符串 用uuid
                token = uuid.uuid4()
                # 2. 存入token表
                # update_or_create()  方法  先查后改,查到就修改,没查到就新增  根据  user 去查
                models.Token.objects.update_or_create(user=ret, defaults={'token': token})
                response['token'] = token
            except ObjectDoesNotExist as exc:
                response['code'] = 101
                response['msg'] = '用户名或密码错误'
            except Exception as e:
                response['code'] = 102
                response['msg'] = str(e)
            return Response(response)
    
    

    不存token的认证

    def get_token(id,salt='123'):
        import hashlib
        md=hashlib.md5()
        md.update(bytes(str(id),encoding='utf-8'))
        md.update(bytes(salt,encoding='utf-8'))
    
        return md.hexdigest()+'|'+str(id)
    
    def check_token(token,salt='123'):
        ll=token.split('|')
        import hashlib
        md=hashlib.md5()
        md.update(bytes(ll[-1],encoding='utf-8'))
        md.update(bytes(salt,encoding='utf-8'))
        if ll[0]==md.hexdigest():
            return True
        else:
            return False
    
    class TokenAuth():
        def authenticate(self, request):
            token = request.GET.get('token')
            success=check_token(token)
            if success:
                return
            else:
                raise AuthenticationFailed('认证失败')
        def authenticate_header(self,request):
            pass
    class Login(APIView):
        def post(self,reuquest):
            back_msg={'status':1001,'msg':None}
            try:
                name=reuquest.data.get('name')
                pwd=reuquest.data.get('pwd')
                user=models.User.objects.filter(username=name,password=pwd).first()
                if user:
                    token=get_token(user.pk)
                    # models.UserToken.objects.update_or_create(user=user,defaults={'token':token})
                    back_msg['status']='1000'
                    back_msg['msg']='登录成功'
                    back_msg['token']=token
                else:
                    back_msg['msg'] = '用户名或密码错误'
            except Exception as e:
                back_msg['msg']=str(e)
            return Response(back_msg)
    from rest_framework.authentication import BaseAuthentication
    class TokenAuth():
        def authenticate(self, request):
            token = request.GET.get('token')
            token_obj = models.UserToken.objects.filter(token=token).first()
            if token_obj:
                return
            else:
                raise AuthenticationFailed('认证失败')
        def authenticate_header(self,request):
            pass
    
    class Course(APIView):
        authentication_classes = [TokenAuth, ]
    
        def get(self, request):
            return HttpResponse('get')
    
        def post(self, request):
            return HttpResponse('post')
    

    总结:

    • 局部使用: 只要在 需要认证的类里面 固定写入你所需的哪些认证(认证类)

       # 这里固定 需要些 这个认证类的列表,列表中存的是 一个一个认证的类
          authentication_classes = [auths.MyAuth,]
      
    • 全局使用: 只要在settings文件中配置 REST_FRAMEWORK 即可

      # settings.py 中配置
      REST_FRAMEWORK = {
          "DEFAULT_AUTHENTICATION_CLASSES": ["app01.auths.MyAuth", ]
      }
      
    • 局部禁用:只要在不需要使用全局使用的认证的 视图类中清除authentication_classes中你不需要的认证即可

      authentication_classes=[]
      

    阅读源码体会:

    • 如果在settings.py 文件中配置了 REST_FRAMEWORK,先默认从项目settings中取
    • 如果取不到,才去默认的drf配置文件中取
    • 用户如果在 视图类中配置了authentication_classes, 则优先从用户配置的取

    ps:先取视图类中的配置的认证--->再取 项目settings文件中配置的----->最后取默认配置

  • 相关阅读:
    JavaScript判断图片是否加载完成的三种方式 (转)
    支付宝异步通知notify_url接收不了问题解决(转)
    支付宝接口使用文档说明 支付宝异步通知(notify_url)与return_url
    Nginx Cache中$request_filename(转)
    win7下搭建nginx+php的开发环境(转)
    Nginx报出504 Gateway Timeout错误2
    BZOJ 3990 [SDOI 2015] 排序 解题报告
    BZOJ 3992 [SDOI 2015] 序列统计 解题报告
    BZOJ 3993 [SDOI 2015] 星际战争 解题报告
    BZOJ 3971 Матрёшка 解题报告
  • 原文地址:https://www.cnblogs.com/qianzhengkai/p/11129867.html
Copyright © 2011-2022 走看看