【DRF-04】rest-framework之认证

  • 1.认证基本使用
    • 1.1:问题:有些API(订单信息)需要用户登录成功之后,才能访问;有些无需登录就能访问。
    • 1.2:解决思路:用户登录后,生成token--保存在数据库中,前端带token,允许访问,不带token,不允许访问。
    • 1.3:models
from django.db import models

class UserInfo(models.Model):
    user_type_choices = (
        (1,'普通用户'),
        (2,'VIP'),
        (3,'SVIP'),
    )
    user_type = models.IntegerField(choices=user_type_choices)
    username = models.CharField(max_length=32,unique=True)
    password = models.CharField(max_length=64)

class UserToken(models.Model):
    user = models.OneToOneField(to='UserInfo',on_delete=models.CASCADE)
    token = models.CharField(max_length=64)
  • 1.4:用户登录并且生成token,自定义认证类
from app01 import models
from rest_framework import exceptions
from django.http import JsonResponse
from rest_framework.authentication import BasicAuthentication
from django.shortcuts import HttpResponse

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):
        self.dispatch()
        ret = {'code':1000,'msg':None}
        try:
            user = request._request.POST.get('username')
            pwd = request._request.POST.get('password')
            obj = models.UserInfo.objects.filter(username=user,password=pwd).first()
            if not obj:
                ret['code'] = 1001
                ret['msg'] = "用户名或密码错误"
            # 为登录用户创建token
            token = md5(user)
            # 存在就更新,不存在就创建
            models.UserToken.objects.update_or_create(user=obj,defaults={'token':token})
            ret['token'] = token
        except Exception as e:
            ret['code'] = 1002
            ret['msg'] = '请求异常'

        return JsonResponse(ret)

class Authtication(BasicAuthentication):
    def authenticate(self, request):
        token = request._request.GET.get('token')
        token_obj = models.UserToken.objects.filter(token=token).first()
        if not token_obj:
            raise exceptions.AuthenticationFailed("用户认证失败")

        # 在rest framework内部会将整个两个字段赋值给request,以供后续操作使用
        return (token_obj.user,token_obj)

    def authenticate_header(self, request):
        pass

class OrderView(APIView):
    # 模拟数据库订单数据
    ORDER_DICT = {
        1:{"name":"电脑","price":12340,"pcs":2},
        2:{"name":"手机","price":1999,"pcs":1}
    }

    authentication_classes = [Authtication,]     # 局部使用

    def get(self,request,*args,**kwargs):
        # request.user   #authenticate 返回的第一个元素
        # request.auth   #authenticate 返回的第二个元素
        ret = {'code':1000,'msg':None,'data':None}
        try:
            ret['data'] = self.ORDER_DICT
        except Exception as e:
            pass

        #son_dumps_params={"ensure_ascii":False} 解决使用JsonResponse返回显示中文
        return JsonResponse(ret,json_dumps_params={"ensure_ascii":False})

  • 1.5:效果展示

  • 2.内置认证类,一般我们自定义的认证类需要继承:BaseAuthentication

  • 3.全局使用

    • 3.1:上述1中是将自定义认证类,局部使用。
    • 3.2:全局使用,注意:认证类不能放到视图中,需要单独放置
    • 3.3:setting
REST_FRAMEWORK = {
    # 全局使用的认证类
    "DEFAULT_AUTHENTICATION_CLASSES":['app01.utils.auth.FirstAuthtication','app01.utils.auth.Authtication', ],
    # "DEFAULT_AUTHENTICATION_CLASSES":['app01.utils.auth.FirstAuthtication', ],
    # "UNAUTHENTICATED_USER":lambda :"匿名用户"
    "UNAUTHENTICATED_USER":None, # 匿名,request.user = None
    "UNAUTHENTICATED_TOKEN":None,# 匿名,request.auth = None
    }
  • 3.4:utils/auth.py
from rest_framework import exceptions
from app01 import models
from rest_framework.authentication import BaseAuthentication

class FirstAuthtication(BaseAuthentication):
    def authenticate(self,request):
        pass

    def authenticate_header(self, request):
        pass

class Authtication(BaseAuthentication):
    def authenticate(self,request):
        token = request._request.GET.get('token')
        token_obj = models.UserToken.objects.filter(token=token).first()
        if not token_obj:
            raise exceptions.AuthenticationFailed('用户认证失败')
        # 在rest framework内部会将整个两个字段赋值给request,以供后续操作使用
        return (token_obj.user, token_obj)

    def authenticate_header(self, request):
        return 'Basic realm="api"'
  • 3.5:如果某个API不需要使用认证,重写authentication_classes 置为空即可
class AuthView(APIView):
    authentication_classes = [ ]
    def post(self,request,*args,**kwargs):
       pass
  • 4.源码流程(重点)
    • 4.1:首先请求进来,走APIView的dispath,对request进行了封装initialize_request;
def dispatch(self, request, *args, **kwargs):
    """
    `.dispatch()` is pretty much the same as Django's regular dispatch,
    but with extra hooks for startup, finalize, and exception handling.
    """
    self.args = args
    self.kwargs = kwargs
    # 对原生的request进行加工(丰富了一些功能)
    # Request(request,parsers=self.get_parsers(),authenticators=self.get_authenticators(),
    #          negotiator=self.get_content_negotiator(),parser_context=parser_context)

    # request(原生的request,MyAuthentication对象)
    # 获取原生request:request._request
    # 获取认证类的对象:request.BasicAuthentication
    # 1.封装request
    request = self.initialize_request(request, *args, **kwargs)
    self.request = request
    self.headers = self.default_response_headers  # deprecate?

    try:
        # 2.认证
        self.initial(request, *args, **kwargs)

        # Get the appropriate handler method
        if request.method.lower() in self.http_method_names:
            handler = getattr(self, request.method.lower(),
                              self.http_method_not_allowed)
        else:
            handler = self.http_method_not_allowed

        response = handler(request, *args, **kwargs)

    except Exception as exc:
        response = self.handle_exception(exc)

    self.response = self.finalize_response(request, response, *args, **kwargs)
    return self.response
  • 4.2:initialize_request方法
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(), #  [Foo(),Bar()]
        negotiator=self.get_content_negotiator(),
        parser_context=parser_context
    )
  • 4.3:get_authenticators方法,authentication_classes如果自己有就取自己的,没有就取配置文件
def get_authenticators(self):
    """
    Instantiates and returns the list of authenticators that this view can use.
    """
    # self.authentication_classes = [Foo,Bar]
    return [auth() for auth in self.authentication_classes]

class APIView(View):
    # The following policies may be set at either globally, or per-view.
    authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
    pass
  • 4.4:initial方法,如果正常就反射执行对应的get/post方法,如果错误,抛出异常。
 def initial(self, request, *args, **kwargs):
      """
      Runs anything that needs to occur prior to calling the method handler.
      """
      self.format_kwarg = self.get_format_suffix(**kwargs)

      # Perform content negotiation and store the accepted info on the request
      neg = self.perform_content_negotiation(request)
      request.accepted_renderer, request.accepted_media_type = neg

      # Determine the API version, if versioning is in use.
      version, scheme = self.determine_version(request, *args, **kwargs)
      request.version, request.versioning_scheme = version, scheme

      # Ensure that the incoming request is permitted
      # 4.实现认证,权限,频率
      self.perform_authentication(request)
      self.check_permissions(request)
      self.check_throttles(request)
  • 4.5:perform_authentication方法
 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
  • 4.6:执行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
  • 4.6:执行_authenticate方法
def _authenticate(self):
    """
    Attempt to authenticate the request using each authentication instance
    in turn.
    """
    # [BasicAuthentication对象,]
    for authenticator in self.authenticators:
        try:
            # 执行认证类的authenticate方法
            # 1.如果authenticate方法抛出异常,self._not_authenticated()执行
            # 2.如果authenticate方法正常,如果有返回值,必须是元祖。self.user, self.auth = user_auth_tuple
            # 3.没报错,也没返回值,返回None。当前这个认证不处理,交给下一个认证处理
            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()
  • 4.7:循环执行authenticate方法,即我们自定义的认证类
posted @ 2024-05-23 21:05  Tony_xiao  阅读(2)  评论(0编辑  收藏  举报