drf : 认证 频率 权限

编写登录功能引出认证,权限,频率:

前端通过接口测试工具Postman将用户名和密码通过HTTP请求发送至Django框架

models.py

from django.db import models


# Create your models here.
class Books(models.Model):
    name = models.CharField(max_length=32)
    price = models.IntegerField()
    publish = models.ForeignKey(to='Publish', on_delete=models.DO_NOTHING)


class Publish(models.Model):
    name = models.CharField(max_length=32)
    addr = models.CharField(max_length=32)


class User(models.Model):
    username = models.CharField(max_length=32)
    password = models.CharField(max_length=32)


"""
用户登录生成随机字符串,响应中携带字符串,发送至服务端后,服务端做逻辑判断。
一般一对一为垂直分表。数据库优化的手段
"""
class UserToken(models.Model):
    user = models.OneToOneField(to='User', on_delete=models.CASCADE)
    token = models.CharField(max_length=64)

模型层拓展:

字段参数可以填写函数名,但不是不能加括号。

views.py

from rest_framework.views import APIView
from . import models
import uuid
from rest_framework.response import Response


class UserAPIView(APIView):
    def post(self,request,*args,**kwargs):
        back_dic = {'code':200,'msg':''}
        username = request.data.get('username')
        password = request.data.get('password')
        # 过滤出user表中含有请求中携带的数据,username,password.
        user = models.User.objects.filter(username=username,password=password).first()
        print(user)

        if user:
            """
            有用户的情况下
            登录成功之后生成token
            user=user,根据user查询,查询不到使用defaults新增,查询到更新。
            """
            # 生成随机字符串,请求中携带随机字符传
        		token = str(uuid.uuid4())
            models.UserToken.objects.update_or_create(defaults={'token': token}, user=user)
            back_dic['msg'] = '随机字符串更新'
            back_dic['token'] = token
        else:
            back_dic['code'] = 201
            back_dic['msg'] = '用户名或者密码错误'
        return Response(back_dic)

此时客户端发送post请求,后端验证正确会产生一条新的token覆盖原有的token。

认证,权限,频率所在位置。

APIView --> dispath(重写了request方法) --> initial(认证,权限,频率)
    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
        """认证"""
        self.perform_authentication(request)
        """权限"""
        self.check_permissions(request)
        """频率"""
        self.check_throttles(request)

认证类的编写

之前提到过,如果代码不属于视图层就另开一个py文件。

认证:只有携带token随机字符串才可以访问某个路由。

urls.py

from app01 import views
from rest_framework.routers import SimpleRouter, DefaultRouter
from django.urls import path, include

router = SimpleRouter()
router.register('books', views.BookAPIView)

urlpatterns = [
    # path('books/',views.BookAPIView.as_view({'get':'login'})),
    path('', include(router.urls)),
    path('login/', views.UserAPIView.as_view())
]

views.py

from .auth import LoginAuth
from rest_framework.generics import ListAPIView
from rest_framework.viewsets import ViewSetMixin


class BookAPIView(ViewSetMixin, ListAPIView):
    queryset = Books.objects.all()
    serializer_class = BookSerializers
    # 增加登录认证
    authentication_classes = [LoginAuth]

auth.py

"""认证类"""
from rest_framework.authentication import BaseAuthentication
from . import models
from rest_framework.exceptions import AuthenticationFailed

class LoginAuth(BaseAuthentication):
    def authenticate(self,request):
        # 写认证规则
        # 取出用户携带的token
        # -注意:取get提交的数据,不要从request.GET中取,从request.query_params中取
        token = request.query_params.get('token')
        print(token)
        # 去数据库查询,token是否存在,存在表示登录了,不存在表示为登录
        user_token = models.UserToken.objects.filter(token=token).first()
        if user_token:
          	# 943ffc46-3826-47f9-a4de-ea9cd4bd23d6
            print(user_token.token)
            # User object (1),模型类对象
            print(user_token.user)
            # junjie
            print(user_token.user.username)
            return user_token.user,user_token.token
        raise AuthenticationFailed('您没有登录,认证失败')

此时想要访问books必须携带token才可以访问。

认证类的全局和局部使用

全局配置:

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES':[
        'app01.auth.LoginAuth'
    ]
}

局部禁用配置:

class UserAPIView(APIView):
    authentication_classes = []

源码分析:

# APIView--->dispathch方法的-----》self.initial(request, *args, **kwargs)(497行)---》APIView的initial方法----》有三句话
self.perform_authentication(request)  # 认证
self.check_permissions(request)       # 权限
self.check_throttles(request)         # 频率



# self.perform_authentication(request)----》新的request对象的.user(是个方法,包装成了数据数属性)---》新的Request类中找到了user方法---》self._authenticate()-----》Request类中的_authenticate
    def _authenticate(self):
        for authenticator in self.authenticators: # 配置在视图类中认证类列表,实例化得到的对象
            try:
                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.authenticators,self是Request类的对象,authenticators属性是Request这个类在实例化的时候,传入的

# Request类在实例化的时候代码
    return Request(
            request,
            parsers=self.get_parsers(),
            authenticators=self.get_authenticators(),  # 要看的
            negotiator=self.get_content_negotiator(),
            parser_context=parser_context
        )


# self.get_authenticators()是APIView的方法
def get_authenticators(self):
   	 	# 列表推导式
        return [auth() for auth in self.authentication_classes]
    	# 返回结果是我们配在视图类中自己写的认证类列表的对象
        return [LoginAuth(),]

权限组件

导入模块

from rest_framework.permissions import BasePermission

auth.py

from rest_framework.permissions import BasePermission

class UserPermission(BasePermission):
    def has_permission(self, request, view):
        """
        认证类走完再走权限类,所以request.user可以拿到当前登录用户
        """
        if request.user.user_type == 1:
            return True
        return False

全局配置使用:

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': [
        'app01.auth.UserPermission',
    ],
}

局部使用:

class BookAPIView(ViewSetMixin, ListAPIView):
    queryset = Books.objects.all()
    serializer_class = BookSerializers
    # 增加登录认证
    authentication_classes = [LoginAuth]
    # 增加权限类
    permission_classes = [UserPermission]

局部禁用:

permission_classes = []

权限类源码解读

APIView --> dispath --> initial --> self.check_permissions(request) --》
def check_permissions(self, request):
  """
  for permission in self.get_permissions(),self.get_permissions()在哪?
  		通过源码可以看到是权限类的全局和局部配置。局部有优先局部,局部没有定义取全局,全局没有去内置。
      def get_permissions(self):
        return [permission() for permission in self.permission_classes]
  """
  for permission in self.get_permissions():
    """
    对应到auth.py中的权限类,如果为True,代码正常执行,如果为False,执行self.permission_denied
    """
    if not permission.has_permission(request, self):
      self.permission_denied(
        request,
        """
        反射,现在auth.py中的权限类找massage.
        """
        message=getattr(permission, 'message', None),
        code=getattr(permission, 'code', None)
      )
# message 自定义报错信息
def permission_denied(self, request, message=None, code=None):
    """
    If request is not permitted, determine what kind of exception to raise.
    """
    if request.authenticators and not request.successful_authenticator:
        raise exceptions.NotAuthenticated()
    raise exceptions.PermissionDenied(detail=message, code=code)

auth.py

class UserPermission(BasePermission):
    def has_permission(self, request, view):
        """
        认证类走完再走权限类,所以request.user可以拿到当前登录用户
        """
        if request.user.user_type == 1:
            return True
        return False

频率组件的使用

频率的作用: 可以对接口访问的频次进行限制,以减轻服务器压力。一般用于付费购买次数,投票等场景使用.

**导入模块: **

from rest_framework.throttling import SimpleRateThrottle

auth.py,写一个类,继承基类,重写get_cache_key方法

class MyThrottle(SimpleRateThrottle):
    # scope一定要写,字符串对应到settings.py中配置REST_FRAMEWORK。
    scope = 'ip_Throttle'

    def get_cache_key(self, request, view):
        # 请求头所包含的信息在META中
        # 127.0.0.1
        print(request.META.get('REMOTE_ADDR'))
        return request.META.get('REMOTE_ADDR')

settings.py,配置文件中一定要配置。

REST_FRAMEWORK = {
	# (一分钟访问三次),key要跟类中的scope对应
    'DEFAULT_THROTTLE_RATES':{
        'ip_Throttle':'3/m'  
    }
}

settings.py ,全局配置。

REST_FRAMEWORK = {
  	 # (一分钟访问三次),key要跟类中的scope对应
    'DEFAULT_THROTTLE_RATES':{
        'ip_Throttle':'3/m'  
    },
 		 # 全局配置
    'DEFAULT_THROTTLE_CLASSES': (
        'app01.auth.MyThrottle',
    ),
}

views.py,视图类中配置

class BookAPIView(ViewSetMixin, ListAPIView):
    # 局部使用
    throttle_classes = [MyThrottle,]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值