zoukankan      html  css  js  c++  java
  • Django:RestFramework之-------认证

    3 restframework-认证

    3.1APIView 认证:

    • 认证是否已经登陆,如果已经登陆返回元组,如果没有登陆报错
    • 源码流程
    执行dispatch方法:
    
    
    def dispatch(self, request, *args, **kwargs):
        #1.封装request    对原生request进行加工,执行initialize_request方法
       	request = self.initialize_request(request, *args, **kwargs)
        try:
            #2.认证
            self.initial(request, *args, **kwargs)
            
    #--------------------initialize_request-------------------
    #执行initialize_request:
    def initialize_request(self, request, *args, **kwargs):
       """
       Returns the initial request object.
       """
       parser_context = self.get_parser_context(request)
    	#返回实例化Request,把原有的request进行封装,丰富功能
        return Request(
        request,
        parsers=self.get_parsers(),
        authenticators=self.get_authenticators(),
        negotiator=self.get_content_negotiator(),
        parser_context=parser_context
        )
    #---------------------------------------------------------
    
    #--------------------Request------------------------------
    #而Request类中:实例化方法中定义:
    class Request:
        def __init__(self, request, parsers=None, authenticators=None,
                     negotiator=None, parser_context=None):
            assert isinstance(request, HttpRequest), (
                'The `request` argument must be an instance of '
                '`django.http.HttpRequest`, not `{}.{}`.'
                .format(request.__class__.__module__, request.__class__.__name__)
            )
    
            self._request = request#1
            self.parsers = parsers or ()
            self.authenticators = authenticators or ()#
            self.negotiator = negotiator or self._default_negotiator()
            self.parser_context = parser_context
            self._data = Empty
            self._files = Empty
            self._full_data = Empty
            self._content_type = Empty
            self._stream = Empty
    #1 : 获取原生request  通过  request._request
    #2 :  获取认证类的对象    通过 request.authenticators
    #返回dispatch方法中继续走
    #---------------------------------------------------------
    
    #--------------------initial----------------------------
    #再initial执行方法:
    	def initial(self, request, *args, **kwargs):
            ...
            #4.实现认证
            self.perform_authentication(request)
    #---------------------------------------------------------
    #--------------------perform_authentication---------------
    #返回当前登陆用户,此时request为封装加工后的request
        def perform_authentication(self, request):
            request.user
    #---------------------------------------------------------
    #---------------------------Request----------------------
    #执行request为Request类中实例化的对象,通过.user执行user方法
    class Request:
        ...
        @property
        def user(self):
           
            if not hasattr(self, '_user'):
                with wrap_attributeerrors():
                    #获取认证对象,进行一步步认证
                    self._authenticate()
            return self._user
    #---------------------------------------------------------
    #----------------_authenticate-------------------------
        def _authenticate(self):
            #authentication_classes=[BasicAuthentication对象,]
           #authenticators为定义authentication_classes=中的对象
        	#循环所有认证对象。
            for authenticator in self.authenticators:
                try:
                    #通过authenticate方法验证是否登陆,如果登陆返回元组,如果未登陆报错
                    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
    #---------------------------------------------------------
    #-------------------------authenticate-----------------------------
        def authenticate(self, request):
            return (self.force_user, self.force_token)
    
    • 那么如何自己定义认证:
    import json
    
    from django.http import HttpResponse
    from django.shortcuts import render
    from rest_framework.decorators import authentication_classes#用于自定义实现认证
    
    from rest_framework.views import APIView
    from rest_framework import exceptions#用于认证失败引发错误
    class MyAuthentication(object):
        #重写authenticate方法
        def authenticate(self,request):
            token = request._request.GET.get('token')
            print(token)
            if not token:
                raise exceptions.AuthenticationFailed("用户认证失败")
            return ("hello",None)
    
        def authenticate_header(self,val):pass
    
    
    class DogView(APIView):
        #自定义认证类
        authentication_classes = [MyAuthentication,]
        def get(self,request,*args,**kwargs):
            ret = {
                "code":1000,
                'msg':"OK"
            }
            return HttpResponse(json.dumps(ret),status=200)
    
        def post(self,request,*args,**kwargs):
            return HttpResponse("创建Dog")
    #www.xxx.com/?token=123
    
    • 效果:

      • 当访问URL为<http://127.0.0.1:8000/dog/

      • 当访问URL为<http://127.0.0.1:8000/dog/?token=123>

    • 基于认证总结:

      源码流程 dispatch开始
      

    3.2 用户登陆

    • models.py

      class UserInfo(models.Model):
          user_type_choice = {
              (1,"普通用户"),
              (2,"VIP"),
              (3,"SVIP")
          }
      
          user_type = models.IntegerField(choices=user_type_choice)
          username = models.CharField(max_length=32,unique=True)
          password = models.CharField(max_length=32)
      
      
      class UserToken(models.Model):
          user = models.OneToOneField(to="UserInfo")
          token = models.CharField(max_length=32)
      
    • views.py

    def md5(user):
        """密码加密"""
        import hashlib
        import time
        ctime = str(time.time())
        m = hashlib.md5(bytes(user,encoding='utf-8'))
        m.update(bytes(ctime,encoding="utf-8"))
        return m.hexdigest()
    
    class AuthView(APIView):
        def post(self,request,*args,**kwargs):
            ret = {"code":1000,"msg":None}
            try:
                username = request._request.POST.get('username')
                password = request._request.POST.get('password')
                obj = models.UserInfo.objects.filter(username=username,password=password).first()
                if not obj:
                    ret["code"] = 1001
                    ret["msg"] = "用户密码错误"
                #为登陆用户创建token
                token = md5(username)
                #存在更新,不存在创建,保存Token
                models.UserToken.objects.update_or_create(user=obj,defaults={"token":token})
    
            except Exception as e:
                ret["code"] = 1002
                ret["msg"] = "请求异常"
    
            return JsonResponse(ret)
    

    3.3用户登陆实现token认证

    #充当数据库数据
    ORDER_DICT = {
        1: {
            'name': '媳妇',
            'age': 18,
            'gender': '男',
            'content': '...'
        },
        2: {
            'name': '老狗',
            'age': 19,
            'gender': '男',
            'content': '....'
        }
    }
    
    
    #自定义认证类
    class Authtication(object):
        def authenticate(self,request):
            token = request._request.GET.get("token")
            token_obj = models.UserToken.objects.filter(token=token).first()
            #如果token表示用户认证失败
            if not token_obj:
                raise exceptions.AuthenticationFailed("用户认证失败")
            #在restframework内部会将这两个字段赋值给request,以供后续使用
            #第一个值通过request.user可以获取
            #第二个值通过reuqest.auth可以获取
            return (token_obj.user,token_obj)
    
        def authenticate_header(self, val): pass
    
    class OrderView(APIView):
        authentication_classes = [Authtication,]
        def get(self,request,*args,**kwargs):
            # token = request._request.GET.get("token")
            # if not token:
            #     return HttpResponse("用户未登陆")
            ret = {"code": 1000, "msg": None, "data": None}
            try:
                ret['data'] = ORDER_DICT
            except Exception as e:
                pass
            return JsonResponse(ret)
    #通过restframework让视图函数只做数据处理,认证交给authentication_classes,代码逻辑清晰不少。
    

    3.4认证流程原理

    • drf认证组件的实现过程:

      as_view() --->view --->执行dispatch方法,
      为自己dispatch方法。通过initialize_request将request对象进行了重新封装。通过initial执行perform_authentication,Request执行user方法去执行_authenticate,遍历循环authentication_classes所有对象。执行认证类的方法.
      	有三种情况:
      		1.如果有返回值,是一个元组 request.user,request.auth
      		2.如果返回None,下一个认证来处理:
      			当都返回None:request.user = AnonymousUser, request.auth = None
      		3.抛出异常,执行self.not_authenticated()
      			
      
    • 当都返回None, request.user = AnonymousUser, request.auth = None.(设置默认值,表示未登陆)

    • 源码中 全局使用配置项:

      将自己定义认证类配置在rest_framework配置项目中
      #当前项目新建文件夹,新建py文件,放入认证类
      settings.py配置
      REST_FRAMEWORK = {
          #全局使用认证类。
      	'DEFAULT_AUTHENTICATION_CLASSES':['api.utils.auth.Authtication',],
      	#设置匿名用户None。
          'UNAUTHENTICATED_USER':None,#request.user = None
          'UNAUTHENTICATED_TOKEN':None,#当前用户未登陆表示匿名用户 request.auth = None
      }
      
      #这样类视图都不用加authentication_classes,也可以执行认证。对于不需要认证的类视图,可以添加未 authentication_classes = []
      

    3.5 django内置认证类

    • 如下:5个类:
    class BaseAuthentication:
        
    class BasicAuthentication(BaseAuthentication):#通过浏览器生成登陆模态框,实现base64加密,把加密后信息放在请求头发过去。
      
    class SessionAuthentication(BaseAuthentication):
    
    class TokenAuthentication(BaseAuthentication):
    
    class RemoteUserAuthentication(BaseAuthentication):
      
    
    • 为了让我们写认证类,更加规范,自定义认证类都必须要继承于BaseAuthentication
    class BaseAuthentication:
        """
        All authentication classes should extend BaseAuthentication.
        """
    
        def authenticate(self, request):
            """
            Authenticate the request and return a two-tuple of (user, token).
            """
            raise NotImplementedError(".authenticate() must be overridden.")
    
        def authenticate_header(self, request):
            #当认证失败时候,浏览器给返回响应头
            """
            Return a string to be used as the value of the `WWW-Authenticate`
            header in a `401 Unauthenticated` response, or `None` if the
            authentication scheme should return `403 Permission Denied` responses.
            """
            pass
    #所有定义认证类必须有authenticate,authenticate_header方法。否则无法正常执行代码
    
  • 相关阅读:
    第一节,Django+Xadmin打造上线标准的在线教育平台—创建用户app,在models.py文件生成3张表,用户表、验证码表、轮播图表
    Tensorflow 错误:Unknown command line flag 'f'
    Python 多线程总结
    Git 强制拉取覆盖本地所有文件
    Hive常用函数 傻瓜学习笔记 附完整示例
    Linux 删除指定大小(范围)的文件
    Python 操作 HBase —— Trift Trift2 Happybase 安装使用
    梯度消失 梯度爆炸 梯度偏置 梯度饱和 梯度死亡 文献收藏
    Embedding 文献收藏
    深度学习在CTR预估中的应用 文献收藏
  • 原文地址:https://www.cnblogs.com/xujunkai/p/11849718.html
Copyright © 2011-2022 走看看