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方法。否则无法正常执行代码
    
  • 相关阅读:
    mytest3.py-api接入平台获取数据
    对比python的进程和线程:多线程是假的
    bug-sqlite3
    安装python3 centos
    句式英语
    java组件学习15天
    areas表-省市区
    中转复制
    追踪路由的2种方法
    干掉头疼的finished with non-zero exit value 2
  • 原文地址:https://www.cnblogs.com/xujunkai/p/11849718.html
Copyright © 2011-2022 走看看