Django REST Framework 认证 - 权限 - 限制

一. 认证

  (你是谁?)

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


自定义Token认证

  第一步 : 建表>>>>

    定义一个用户表和一个保存用户Token的表

class UserInfo(models.Model):
    username = models.CharField(max_length=16)
    password = models.CharField(max_length=32)
    type = models.SmallIntegerField(
        choices=((0, '普通用户'), (1, 'VIP用户')),
        default=0
    )


class Token(models.Model):
    user = models.OneToOneField(to='UserInfo')
    token_code = models.CharField(max_length=128)

 

  第二步: 定义一个登陆视图 >>>>

from rest_framework.views import APIView
from app2 import models
from rest_framework.response import Response
import hashlib, time

def get_random_token(username):
    """
    根据用户名和时间戳生成随机token
    """
    timestamp = str(time.time())
    m = hashlib.md5(bytes(username, encoding="utf-8"))
    m.update(bytes(timestamp, encoding="utf-8"))
    return m.hexdigest()


class LoginView(APIView):
    """
    校验用户名是否正确从而生成token的视图
    """
    def post(self, request):
        res = {"code": 0}
        # print(request.data)
        username = request.data.get("username")
        password = request.data.get("password")

        user = models.UserInfo.objects.filter(username=username, password=password).first()
        if user:
            token = get_random_token(username)
            models.Token.objects.update_or_create(defaults={"token_code": token}, user=user)
            res["token"] = token
        else:
            res["code"] = 1
            res["error"] = "用户名或密码错误"
        return Response(res)

  第三步: 定义一个认证类 >>>>

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


class MyAuth(BaseAuthentication):
    def authenticate(self, request):
        # if request.method in ["POST", "PUT", "DELETE"]:  
        
        # 如果在表单中需要判断请求方式    由于表单是post请求,所以获取token 的方式为 request.data.get("token")
        # request.query_params为url中的参数
        request_token = request.query_params.get("token", None)  
        if not request_token:
            raise AuthenticationFailed('q.缺少token')

        token_obj = models.Token.objects.filter(token_code=request_token).first()
        if not token_obj:
            raise AuthenticationFailed("无效的Token")
        # token_obj.user 通过token这张表的对象和user这个关联字段 找到 UserInfo表的对象及当前用户对象
        return token_obj.user, request_token
        
        # else:
        #     return None, None

  第三步: 使用认证类 >>>>

    视图级别认证

class CommentViewSet(ModelViewSet):

    queryset = models.Comment.objects.all()
    serializer_class = app01_serializers.CommentSerializer
    authentication_classes = [MyAuth, ]

    全局级别认证

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

 

二. 权限

  (你能干什么?) 设置只有VIP才能看到的东西

第一步: 自定义一个权限类

"""
自己动手写一个权限组件
"""
from rest_framework.permissions import BasePermission


class MyPermission(BasePermission):

    message = '只有VIP才能访问'

    def has_permission(self, request, view):
        # 认证类中返回了token_obj.user, request_token
        # request.auth 等价于request_token
        if not request.auth:
            return False
        # request.user为当前用户对象
        if request.user and request.user.type == 1:  # 如果是VIP用户
            print("requ", request.user, type(request.user))
            return True
        else:
            return False

第二步: 使用

  视图级别配置

class CommentViewSet(ModelViewSet):

    queryset = models.Comment.objects.all()
    serializer_class = app01_serializers.CommentSerializer
    authentication_classes = [MyAuth, ]
    permission_classes = [MyPermission, ]

  全局级别设置

# 在settings.py中设置rest framework相关配置项
REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": ["app01.utils.MyAuth", ],
    "DEFAULT_PERMISSION_CLASSES": ["app01.utils.MyPermission", ]
}

三. 控制

  (你一分钟能干多少次?)**好像有点污~~

第一步: 自定义限制类 >>>>

import time

# from rest_framework.throttling import
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:
            # 判断之前有没有访问记录(第一次来)
            self.history.insert(0, now)
            return True

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


# 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']

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

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

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

第二步: 使用 >>>

  视图中使用

class CommentViewSet(ModelViewSet):

    queryset = models.Comment.objects.all()
    serializer_class = app01_serializers.CommentSerializer
    throttle_classes = [MyThrottle, ]

  全局中使用

# 在settings.py中设置rest framework相关配置项
REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": ["app01.utils.MyAuth", ],
    "DEFAULT_PERMISSION_CLASSES": ["app01.utils.MyPermission", ]
    "DEFAULT_THROTTLE_CLASSES": ["app01.utils.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.py中设置rest framework相关配置项
REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": ["app01.utils.MyAuth", ],
    # "DEFAULT_PERMISSION_CLASSES": ["app01.utils.MyPermission", ]
    "DEFAULT_THROTTLE_CLASSES": ["app01.utils.VisitThrottle", ],
    "DEFAULT_THROTTLE_RATES": {
        "xxx": "5/m",
    }
}

看源码:

  1.认证流程

 

  2. 权限

 

猜你喜欢

转载自www.cnblogs.com/konghui/p/10279411.html