zoukankan      html  css  js  c++  java
  • 002---rest_framework认证组件

    rest_framework认证组件

    问题:有些API,需要用户登录才能访问,有些无需登录就能访问。

    解决:

    • a. 创建两个模型类:UserInfo---OneToOne---UsetToken

    • b. 登录成功后保存在数据库的UserToken表中

              # urls.py
      
              from django.urls import path
              from api import views
      
      
              urlpatterns = [
                  path('api/v1/auth/',views.AuthView.as_view()),
              ]
      
              # views.py
      
              import hashlib, time
              from rest_framework.views import APIView
              from rest_framework.response import Response
              from api import models
      
              def md5(username):
                  """
                  生成token
                  :param username:
                  :return:
                  """
                  m = hashlib.md5(username.encode('utf-8'))
                  m.update(str(time.time()).encode('utf-8'))
                  return m.hexdigest()
      
              class AuthView(APIView):
                  """
                  用户登录
                  """
      
                  def post(self, request, *args, **kwargs):
                      ret = {'code': 1000, 'msg': None}
                      username = request._request.POST.get('username')
                      password = request._request.POST.get('password')
      
                      user = models.UserInfo.objects.filter(username=username, password=password).first()
                      if not user:
                          ret['msg'] = '用户名或密码错误'
                          return Response(ret)
                      token = md5(username)
                      # 存在就更新,不存在就创建
                      models.UserToken.objects.update_or_create(user=user, defaults={'token': token})
                      ret['code'] = 2000
                      ret['msg'] = '登录成功'
                      ret['token'] = token
                      return Response(ret)
      
      
    • c. 创建自定义Auth认证类和订单,访问订单,判断是否携带token访问,不然不让访问

    
              # urls.py
    
              urlpatterns = [
                  path('api/v1/auth/',views.AuthView.as_view()),
                  path('api/v1/order/',views.OrderView.as_view()),
              ]
    
              # views.py
    
              ORDERS = [
                  {
                      'id': '1',
                      'name': '华为MATE 20 Pro',
                      'price': 6999,
                  },
                  {
                      'id': '2',
                      'name': '一加6t',
                      'price': 3999,
                  },
                  {
                      'id': '3',
                      'name': '狗比亚',
                      'price': 2699,
                  },
              ]
    
              class Authentication(object):
                  """
                  自定义认证类
                  """
    
                  def authenticate(self, request):
                      token = request._request.GET.get('token')
                      token_obj = models.UserToken.objects.filter(token=token).first()
                      if not token_obj:
                          raise AuthenticationFailed('用户认证失败')
                      # 返回一个元祖 一个是用户对象。一个是token对象
                      return token_obj.user, token_obj
    
                  def authenticate_header(self, request):
                      pass
    
    
              class OrderView(APIView):
                  """
                  订单相关业务
                  """
                  authentication_classes = [Authentication]
    
                  def get(self, request, *args, **kwargs):
                      ret = {'code': 2000, 'msg': '获取成功', 'data': ORDERS}
                      return Response(ret)
    
    
    • d. 测试接口:只测试了正确的用户名和密码,错误的自己测

              import requests
      
              auth_url = 'http://127.0.0.1:8000/api/v1/auth/'
              res = requests.post(auth_url, data={'username': 'alex', 'password': 'root1234'}).json()
      
              print(res)  # {'code': 2000, 'msg': '登录成功', 'token': '02fdedaa2c6c5773e00706b5890e289a'}
              token = res.get('token')
      
              order_url = 'http://127.0.0.1:8000/api/v1/order/'
              res = requests.get(order_url,params={'token':token}).json()
              print(res)  # {'code': 2000, 'msg': '获取成功', 'data': [{'id': '1', 'name': '华为MATE 20 Pro', 'price': 6999}, {'id': '2', 'name': '一加6t', 'price': 3999}, {'id': '3', 'name': '狗比亚', 'price': 2699}]}
      
      
    • e. 再写一个API用户获取用户信息(通过认证通过后注册的request.user对象和request.token对象)

      
              urlpatterns = [
                  path('api/v1/auth/',views.AuthView.as_view()),
                  path('api/v1/order/',views.OrderView.as_view()),
                  path('api/v1/user_info/',views.UserInfoView.as_view()),
              ]
      
              class UserInfoView(APIView):
                  """
                  获取用户信息相关
                  """
                  authentication_classes = [Authentication]
      
                  def get(self, request, *args, **kwargs):
                      data = {
                          'username': request.user.username,
                          'token': request.auth.token
                      }
                      ret = {'code': 2000, 'msg': '获取成功', 'data': data}
                      return Response(ret)
      
    • f. 测试认证成功之后返回元祖的两个对象

      
              import requests
      
              auth_url = 'http://127.0.0.1:8000/api/v1/auth/'
              res = requests.post(auth_url, data={'username': 'alex', 'password': 'root1234'}).json()
      
              print(res)  # {'code': 2000, 'msg': '登录成功', 'token': '05810e2756bc6cc27c1ce3af827aa45f'}
              token = res.get('token')
      
              order_url = 'http://127.0.0.1:8000/api/v1/user_info/'
              res = requests.get(order_url,params={'token':token}).json()
              print(res)  # {'code': 2000, 'msg': '获取成功', 'data': {'username': 'alex', 'token': '05810e2756bc6cc27c1ce3af827aa45f'}}
      

    全局使用(再次看源码)

          def get_authenticators(self):    
              return [auth() for auth in self.authentication_classes]
              
          authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
            
          def reload_api_settings(*args, **kwargs):
              setting = kwargs['setting']
              if setting == 'REST_FRAMEWORK':
                  api_settings.reload()
              # 所以通过配置api.settings可达到全局使用的效果
    
    
    • 全局使用
       # 在api应用下面创建utilsauth.py 把之前写的自定义类剪切过来
       # utilsauth.py
       from api import models
       from rest_framework.exceptions import AuthenticationFailed
       from rest_framework.authentication import BaseAuthentication
            
            
        class Authentication(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 AuthenticationFailed('用户认证失败')
                # 返回一个元祖 一个是用户对象。一个是token对象
                return token_obj.user, token_obj
        
            def authenticate_header(self, request):
                pass  
        # settings.py
        ##############rest_framework配置################
        REST_FRAMEWORK = {
            'DEFAULT_AUTHENTICATION_CLASSES':['api.utils.auth.Authentication']
        }
        # views.py
        import hashlib, time
        from rest_framework.views import APIView
        from rest_framework.response import Response
        from api import models
        
        ORDERS = [
            {
                'id': '1',
                'name': '华为MATE 20 Pro',
                'price': 6999,
            },
            {
                'id': '2',
                'name': '一加6t',
                'price': 3999,
            },
            {
                'id': '3',
                'name': '狗比亚',
                'price': 2699,
            },
        ]
        
        
        def md5(username):
            """
            生成token
            :param username:
            :return:
            """
            m = hashlib.md5(username.encode('utf-8'))
            m.update(str(time.time()).encode('utf-8'))
            return m.hexdigest()
        
        
        class AuthView(APIView):
            """
            用户登录
            """
            # 不需要认证为空列表即可
            authentication_classes = []
        
            def post(self, request, *args, **kwargs):
                ret = {'code': 1000, 'msg': None}
                username = request._request.POST.get('username')
                password = request._request.POST.get('password')
        
                user = models.UserInfo.objects.filter(username=username, password=password).first()
                if not user:
                    ret['msg'] = '用户名或密码错误'
                    return Response(ret)
                token = md5(username)
                # 存在就更新,不存在就创建
                models.UserToken.objects.update_or_create(user=user, defaults={'token': token})
                ret['code'] = 2000
                ret['msg'] = '登录成功'
                ret['token'] = token
                return Response(ret)
        
        
        class OrderView(APIView):
            """
            订单相关业务
            """
        
            def get(self, request, *args, **kwargs):
                ret = {'code': 2000, 'msg': '获取成功', 'data': ORDERS}
                return Response(ret)
        
        
        class UserInfoView(APIView):
            """
            获取用户信息相关
            """
        
            def get(self, request, *args, **kwargs):
                data = {
                    'username': request.user.username,
                    'token': request.auth.token
                }
                ret = {'code': 2000, 'msg': '获取成功', 'data': data}
                return Response(ret)
    
    

    梳理:

    • 全局使用:创建自定义认证类,继承BaseAuthentication,实现:authenticate()

    • 局部使用(使用自带的认证类):authentication_classes = [BasicAuthentication,]

    • 该方法有三种返回值

      1. None 当前认证类不做认证,交给下一个认证来来执行
      2. 抛出异常 raise AuthenticationFailed('用户认证失败')
      3. 元祖(元素1,元素2):元素1赋值给request.user;元素2赋值给request.token
    • 注意:当所有认证类都返回None时,此时是匿名用户,默认是AnonymousUser,可以通过配置文件来更改显示

        REST_FRAMEWORK = {
        "DEFAULT_AUTHENTICATION_CLASSES":['api.utils.auth.FirstAuthtication','api.utils.auth.Authtication', ],
        #"UNAUTHENTICATED_USER":lambda :"匿名用户",
        "UNAUTHENTICATED_USER":None, # 匿名,request.user = None
        "UNAUTHENTICATED_TOKEN":None,# 匿名,request.auth = None
        }
    

    源码流程

    1. dispath()-->initialize_request()
      - 封装request对象,获取认证类(全局&局部),通过列表生成式创建认证类对象

    2. initial-->perform_authentication()-->request.user
      - for循环每个认证类 return

    3. 认证成功基于反射根据method不同做不同处理

  • 相关阅读:
    【闲暇研究】某问道,问问,道道宠物素材整理
    小米手机刷机,官方教程。
    【闲暇研究】某问道,每日极品道具体验礼包。
    【闲暇研究】某问道,悟道只能用银元宝。
    Windows Server 2016从Evaluation评估版转换成正式版
    【闲暇研究】某问道,仙元、道具合成的修改,与参考文件。
    关于小米手机安装APP总是提示“与已安装应用签名不同”的问题
    WebStorm的快捷键,技巧
    Webstorm(常见配置+Emmet简单语法)
    05-方法重写
  • 原文地址:https://www.cnblogs.com/xjmlove/p/10394850.html
Copyright © 2011-2022 走看看