DRF认证权限频率

认证

DRF提供了关于认证的简单书写方法。

比如用户需要登录后才能访问某个视图:

模型层models.py:

class User(models.Model):
    # 用户名
    username = models.CharField(max_length=16)
    # 密码
    password = models.CharField(max_length=16)
    
# 存储随机字符串用,模仿session
class UserToken(models.Model):
    token = models.CharField(max_length=32)
    user = models.OneToOneField(to=User, on_delete=models.CASCADE)

认证类:

from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed

class UserAuth(BaseAuthentication):
    def authenticate(self, request):
        # 假设随机字符串在get请求中
        token = request.query_params.get('token')
        # 查看获取的随机字符串是否和数据库中的匹配
        user_token = UserToken.objects.filter(token=token).first()
        if user_token:
            # 如果匹配,返回两个值:user对象和随机字符串
            return user_token.user, token
        # 如果不匹配,代表未登录,抛异常
        raise AuthenticationFailed('你还未登录')

视图类(局部配置):

class UserView(APIView):

    def post(self, request):
        # 用户名
        username = request.data.get('username')
        # 密码
        password = request.data.get('password')
        # 获取用户对象
        user_obj = User.objects.filter(username=username, password=password).first()
        # 判断是否获取到用户对象
        if user_obj:
            # 获取随机字符串,uuid为内置模块,可生成随机长字符串
            token = str(uuid.uuid4())
            # 如果数据库中已有token数据,那么就更新,否则添加新token
            UserToken.objects.update_or_create(defaults={'token': token}, user=user_obj)
            return Response({'code': 100, 'msg': '登录成功', 'token': token})
        return Response({'code': 99, 'msg': '用户名或密码错误'})

# 用于测试用户是否已登录用
class BookView(APIView):
    # 重写属性,把认证类添加进去(局部配置)
    authentication_classes = [UserAuth, ]

    def get(self, request):
        return Response('BookView get')

路由层:

urlpatterns = [
    path('books/', views.BookView.as_view()),
    path('login/', views.UserView.as_view()),
]

先登录,获取token字符串

向book/发送请求,如果没携带token,或携带错误的token,会显示未登录

携带正确的token才能正确访问

全局配置

在配置文件中添加:

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        '认证类路径'
    ],
}

认证实现原理(源码剖析)

认证、权限、频率三大认证都是在APIView类中实现的。

具体实现通过APIView类中的dispatch()方法:


认证实现方法查看:

user方法,位置在DRF的Request类中,被封装成数据的形式

_authenticate()方法

self.authenticators是在Request实例化的时候传入的,也是在dispatch中实现的



权限

权限的设置与认证的书写十分相似。

模型层:

class User(models.Model):
    # 用户名
    username = models.CharField(max_length=16)
    # 密码
    password = models.CharField(max_length=16)
    # 权限
    role = models.IntegerField(choices=((1, '管理员'), (2, 'VIP'), (3, '普通用户')), default=3)
    
# 存储随机字符串用,模仿session
class UserToken(models.Model):
    token = models.CharField(max_length=32)
    user = models.OneToOneField(to=User, on_delete=models.CASCADE)

权限类:

from rest_framework.permissions import BasePermission
class UserPermission(BasePermission):
    # 权限不够时返回的信息
    message = '只有管理员才能访问'
    # 重写方法
    def has_permission(self, request, view):
        """view为当前视图"""
        # 获取get数据
        permission = request.query_params.get('permission')
        # 在认证类中返回了user对象,这个对象就是request.user
        if request.user.role == 1:
            return True
        return False

视图类(局部配置):

class UserView(APIView):
    authentication_classes = [UserAuth, ]
    # 局部配置权限
    permission_classes = [UserPermission, ]

    def get(self, request):
        return Response('BookView get')

全局配置

配置文件中添加:

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': [
        '权限类路径',
    ],
}

权限实现原理(源码剖析)

在认证实现原理中了解到:initial方法实现了三大校验

查看check_permissions是怎么实现的

self.get_permissions():

频率

频率,即用户访问服务器的次数。DRF提供了限制频率的类。

频率类编写:

from rest_framework.throttling import SimpleRateThrottle

class MyThrottling(SimpleRateThrottle):
    # 名字随意,之后要去配置文件配置
    scope = 'rate'
    # 需要重写方法
    def get_cache_key(self, request, view):
        """这个方法返回什么,就根据什么进行限制"""
        # 返回客户端的ip,即限制ip的访问次数
        return request.META.get('REMOTE_ADDR')

视图类(局部配置):

class MyView(APIView):
    # 重写APIView提供的throttle_classes属性,频率类写进去
    throttle_classes = [MyThrottling, ]

    def get(self, request):
        return Response('MyView')

setting.py配置文件添加:

REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_RATES': {
        # 这里的键为频率类中的scope属性值
        'rate': '3/m'  # 限制一分钟只能访问三次,h(时)、s(秒)、d(天)
    }
}

全局配置

setting.py配置文件添加:

REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_RATES': {
        # 这里的键为频率类中的scope属性值
        'rate': '3/m'  # 限制一分钟只能访问三次,h(时)、s(秒)、d(天)
    },
    # 全局生效
    'DEFAULT_THROTTLE_CLASSES':['频率类路径']
}

设置提示消息

超出限制的次数后提示消息默认返回的是英文,如果想要自定义消息,需要在视图类中配置:

class MyView(APIView):
    # 重写APIView提供的throttle_classes属性,频率类写进去
    throttle_classes = [MyThrottling, ]

    def get(self, request):
        return Response('MyView')
    # 固定写法
    def throttled(self, request, wait):
        from rest_framework.exceptions import Throttled
        class MyThrottled(Throttled):
            # 默认消息
            default_detail = '超限制了,'
            # wait参数为单数时显示的内容
            extra_detail_singular = '还有最后{wait}秒。'
            # wait参数为复数时显示的内容
            extra_detail_plural = '还有{wait}秒。'
        raise MyThrottled(wait)

自定义频率类

根据IP地址进行限制,一分钟只能访问3次

写法一:

import time
class MyThrottling():
    history = {}
    wait_time = None
    # 一定要有allow_request方法
    def allow_request(self, request, view):
        # 获取IP地址
        client_ip = request.META.get('REMOTE_ADDR')
        # 判断客户端是否是第一次发送请求
        if client_ip not in self.history:
            self.history[client_ip] = [time.time()]
            return True
        # 去除列表中距当前时间超过出60秒的
        for t in self.history[client_ip]:
            if time.time() - t > 60:
                self.history[client_ip].remove(t)
        # 判断客户端一分钟内访问次数
        if len(self.history[client_ip]) < 3: 
            self.history[client_ip].append(time.time())
            return True
        # 需要等待的时间
        self.wait_time = 60 - (time.time() - self.history[client_ip][0])
        return False
    # wait返回需要等待的时间
    def wait(self):
        return self.wait_time

写法二(优化写法):

class MyThrottling():
    VISIT_RECORD = {}

    def __init__(self):
        self.history = None
    def allow_request(self, request, view):
        # (1)取出访问者ip
        ip = request.META.get('REMOTE_ADDR')
        import time
        ctime = time.time()
        # (2)判断当前ip不在访问字典里,添加进去,并且直接返回True,表示第一次访问
        if ip not in self.VISIT_RECORD:
            self.VISIT_RECORD[ip] = [ctime, ]
            return True
        self.history = self.VISIT_RECORD.get(ip)
        # (3)循环判断当前ip的列表,有值,并且当前时间减去列表的最后一个时间大于60s,把这种数据pop掉,这样列表中只有60s以内的访问时间,
        while self.history and ctime - self.history[-1] > 60:
            self.history.pop()
        # (4)判断,当列表小于3,说明一分钟以内访问不足三次,把当前时间插入到列表第一个位置,返回True,顺利通过
        # (5)当大于等于3,说明一分钟内访问超过三次,返回False验证失败
        if len(self.history) < 3:
            self.history.insert(0, ctime)
            return True
        else:
            return False

    def wait(self):
        import time
        ctime = time.time()
        return 60 - (ctime - self.history[-1])

使用自定义的频率类不需要去配置文件配置,直接在视图类中使用即可。

频率实现原理(源码剖析)

APIView类中的check_throttles校验频率:

self.throttled(request, duration):用于抛出异常

内置认证、权限、频率类

内置认证类

SessionAuthentication:Session认证方式,采用session的方式进行校验。

from rest_framework.authentication import SessionAuthentication

BasicAuthentication:基本认证方式,采用用户名和密码校验。

from rest_framework.authentication import BasicAuthentication

TokenAuthentication:token认证方式,采用token值进行校验。

from rest_framework.authentication import TokenAuthentication

内置权限类

IsAdminUser:只有auth的超级管理员才会有权限(需要用到auth表)。

from rest_framework.permissions import IsAdminUser

IsAuthenticated:只有已登录用户才会有权限。

from rest_framework.permissions import IsAuthenticated

IsAuthenticatedOrReadOnly:只有已登录用户或者发送的请求是('GET', 'HEAD', 'OPTIONS')其中一个才有权限。

from rest_framework.permissions import IsAuthenticatedOrReadOnly

内置频率类

UserRateThrottle:限制登录用户,需要配置配置文件。

from rest_framework.throttling import UserRateThrottle
# UserRateThrottle的scope属性
scope = 'user'

AnonRateThrottle:限制未登录用户,需要配置配置文件。

from rest_framework.throttling import AnonRateThrottle
# AnonRateThrottle的scope属性
scope = 'anon'
posted @ 2022-06-20 18:14  Yume_Minami  阅读(73)  评论(0编辑  收藏  举报