Fork me on GitHub

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方法。否则无法正常执行代码
posted @ 2019-11-13 17:27  是阿凯啊  阅读(200)  评论(0编辑  收藏  举报