• 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. 权限

     

  • 相关阅读:
    [BZOJ] 2276: [Poi2011]Temperature
    [Codevs] 5037 线段树练习4加强版
    [Codevs] 4919 线段树练习4
    [Codevs] 1082 线段树练习3
    [Codevs] 1080 线段树练习
    [Codevs] 1081 线段树练习 2 ----“分块!”
    1629: [Usaco2007 Demo]Cow Acrobats
    Kruskal || BZOJ 1601: [Usaco2008 Oct]灌水 || Luogu P1550 [USACO08OCT]打井Watering Hole
    SET || BZOJ 1588: [HNOI2002]营业额统计 || Luogu P2234 [HNOI2002]营业额统计
    线段树合并+并查集 || BZOJ 2733: [HNOI2012]永无乡 || Luogu P3224 [HNOI2012]永无乡
  • 原文地址:https://www.cnblogs.com/konghui/p/10279411.html
Copyright © 2020-2023  润新知