DRF的认证、权限 和 限制

一、概述

认证是将传入请求与一组标识凭据(例如请求来自的用户或其签名的令牌)相关联的机制。然后 权限 和 限制 组件决定是否拒绝这个请求。

简单来说就是:

认证确定了你是谁

权限确定你能不能访问某个接口

限制确定你访问某个接口的频率

二、认证

REST framework 提供了一些开箱即用的身份验证方案,并且还允许你实现自定义方案。

三、自定义Token认证

model 定义一个包含token字段的用户表

class UserInfo(models.Model):
    name = models.CharField(max_length=32)
    pwd = models.CharField(max_length=32)
    vip = models.BooleanField(default=False)
    token = models.CharField(max_length=128, null=True, blank=True)

定义登录视图:

class LoginView(APIView):
    def post(self, request):
        name = request.data.get('name')
        pwd = request.data.get('pwd')
        if name and pwd:
            user_obj = models.UserInfo.objects.filter(name=name, pwd=pwd).first()
            if user_obj:
                # 登陆成功
                # 生成token(时间戳 + Mac地址)
                token = uuid.uuid1().hex
                # 1.保存在用户表中
                user_obj.token = token
                user_obj.save()
                # 2.给用户返回
                return Response({'error_no': 0, 'token': token})

            else:
                # 用户名或密码错误
                return Response({'error_no': 1, 'error': '用户名或密码错误'})
        else:
            return Response('无效的参数')

创建auth.py自定义一个认证类

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


class MyAuth(BaseAuthentication):
    '''自定义认证类'''

    def authenticate(self, request):
        token = request.query_params.get ('token')
        if token:
            # 如果请求url中携带有token参数
            user_obj = models.UserInfo.objects.filter(token=token).first()
            if user_obj:
                # token 是有效的,返回一个元组
                return user_obj, token  # request.user, request.auth
            else:
                raise AuthenticationFailed('无效的token')
        else:
            raise AuthenticationFailed('请求的URL中必须携带token参数')

局部配置认证

在views中自定义一个测试类TestAuthView

from auth_demo.auth import MyAuth
from auth_demo.permissions import MyPermission
# 登录之后才能看到数据接口
class TestAuthView(APIView):
    authentication_classes = [MyAuth, ]  # 局部配置认证

    def get(self, request):
        print(request.user.name)
        print(request.auth)
        return Response('这个视图里面的数据只有登录后才能看到!')

全局配置认证

# 在settings.py中配置
REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": ["auth_demo.auth.MyAuth", ] 
}

 四、权限

自己动手写一个权限组件

from rest_framework.permissions import BasePermission


class Mypermission(BasePermission):
    message = '只有VIP才能访问'

    def has_permission(self, request, view):
        # vip才有访问权限
        # request.user:当前经过认证的用户对象
        # 如果没有认证 request.user就是匿名用户
        if not request.auth:
            # 认证没有通过
            return False
        if request.user.vip:
            return True
        else:
            return False

视图级别配置

from auth_demo.auth import MyAuth
from auth_demo.permissions import Mypermission
# 登录之后才能看到数据接口
class TestAuthView(APIView):
    authentication_classes = [MyAuth, ]  # 局部配置认证
    permission_classes = [Mypermission, ] # 试图级别配置权限

    def get(self, request):
        print(request.user.name)
        print(request.auth)
        return Response('这个视图里面的数据只有登录后才能看到!')

全局级别设置

只需要在settings的DRF配置项中添加权限类

# DRF的配置
REST_FRAMEWORK = {
    # 配置默认使用的版本控制类
    'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning',
    # 'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.QueryParameterVersioning',# url参数控制版本
    'DEFAULT_VERSION': 'v1',  # 默认的版本
    'ALLOWED_VERSIONS': ['v1', 'v2'],  # 有效的版本
    'VERSION_PARAM': 'version',  # 版本的参数名与URL conf中一致
    # 自定义认证
    'DEFAULT_AUTHENTICATION_CLASSES': ['auth_demo.auth.MyAuth', ],
    # 自定义权限
    'DEFAULT_PERMISSION_CLASSES': ['auth_demo.permissions.MyPermission', ]
}

五、限制

DRF内置了基本的限制类,首先自己动手写一个限制类,熟悉限制组件的执行过程。

自定义限制10秒内只能访问3次

自定义的限制类必须实现2个方法

allow_request() 和 wait()
分析:
# history = ['9:56:12', '9:56:10', '9:56:09', '9:56:08']  # '9:56:18' - '9:56:12'

# history = ['9:56:19', '9:56:18', '9:56:17', '9:56:08']

# 最后一项到期的时间就是下一次允许请求的时间

# 最后一项到期的时间:history[-1] + 10

# 最后一项还剩多少时间过期
# history[-1] + 10 - now

实现:

import time

# 存放访问记录信息
visit_record= {}
class MyThrottle(object):
    def __init__(self):
        self.history = None

    def allow_request(self, request, view):
        # 拿到当前请求的ip作为访问记录的key
        ip = request.META.get('REMOTE_ADDR')
        # 拿到当前请求的时间戳
        now = time.time()
        if ip not in visit_record:
            visit_record[ip] = []
        # 把当前请求的访问记录拿出来保存到一个变量中
        history = visit_record[ip]
        self.history = history
        # 循环访问历史,把超过10秒中的请求时间去掉
        while history and now - history[-1] > 10:
            history.pop()
        # 此时 history中只保存了最近10秒中的访问记录
        if len(history) >= 3:
            # 限制十秒钟内最多只能访问三次
            return False
        else:
            # 将当前访问时间插入history的首位
            self.history.insert(0, now)
            return True

    def wait(self):
        '''告诉客户还需等待多久'''
        now = time.time()
        return self.history[-1] + 10 - now

单个视图类使用

from auth_demo.auth import MyAuth
from auth_demo.permissions import MyPermission
from auth_demo.throttle import MyThrottle
# 登录之后才能看到数据接口
class TestAuthView(APIView):
    authentication_classes = [MyAuth, ]  # 局部配置认证
    permission_classes = [MyPermission, ] # 视图级别配置权限
    throttle_classes = [MyThrottle, ] # 限制单个视图访问频率

    def get(self, request):
        print(request.user.name)
        print(request.auth)
        return Response('这个视图里面的数据只有登录后才能看到!')

全局使用

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': ['auth_demo.auth.MyAuth', ],
    'DEFAULT_PERMISSION_CLASSES': ['auth_demo.permissions.MyPermission', ]
    
"DEFAULT_THROTTLE_CLASSES": ["auth_demo.throttle.MyThrottle", ] }

使用内置限制类

from rest_framework.throttling import SimpleRateThrottle


class VisitThrottle(SimpleRateThrottle):

    scope = "xxx"

    def get_cache_key(self, request, view):
        return self.get_ident(request)

全局配置

settings的REST_FRAMEWORK配置中加一个:
REST_FRAMEWORK = {
    # 限制类
    "DEFAULT_THROTTLE_CLASSES": ["auth_demo.throttle.VisitThrottle", ],
    "DEFAULT_THROTTLE_RATES": {
        "xxx": "1/s",
    },
}

 

posted @ 2019-01-16 19:42  清风_Z  阅读(738)  评论(1编辑  收藏  举报