• python之路_rest-framework之认证、权限、频率


    一、认证

    1、自定义认证

      认证即是通过继承BaseAuthentication重构认证的类,认证的逻辑在类的authenticate方法中实现,通过判断用户传递的token,如果认证成功返回(用户,用户Token)元组,会将用户对象封装到request里,通过request.用户可以获得用户的相关信息。具体代码如下:

    from rest_framework.authentication import BaseAuthentication
    from rest_framework import exceptions
    
    token_list = [
        'sfsfss123kuf3j123',
        'asijnfowerkkf9812',
    ]
    
    class TestAuthentication(BaseAuthentication):
        def authenticate(self, request):
            """
            用户认证,如果验证成功后返回元组: (用户,用户Token)
            :param request: 
            :return: 
                None,表示跳过该验证;
                    如果跳过了所有认证,默认用户和Token和使用配置文件进行设置
                    self._authenticator = None
                    if api_settings.UNAUTHENTICATED_USER:
                        self.user = api_settings.UNAUTHENTICATED_USER()
                    else:
                        self.user = None
    
                    if api_settings.UNAUTHENTICATED_TOKEN:
                        self.auth = api_settings.UNAUTHENTICATED_TOKEN()
                    else:
                        self.auth = None
                (user,token)表示验证通过并设置用户名和Token;
                AuthenticationFailed异常
            """
            val = request.query_params.get('token')
            if val not in token_list:
                raise exceptions.AuthenticationFailed("用户认证失败")
    
            return ('登录用户', '用户token')
    
        def authenticate_header(self, request):
            """
            Return a string to be used as the value of the `WWW-Authenticate`
            header in a `401 Unauthenticated` response, or `None` if the
            authentication scheme should return `403 Permission Denied` responses.
            """
            # 验证失败时,返回的响应头WWW-Authenticate对应的值
            pass

    2、局部认证

      上述为我们的重构的一个认证的实例。但是怎么才能用到我们需要进行认证的视图上呢?通过在具体视图中配置此认证类即可,具体实例如下:

    方式一:

    from rest_framework.views import APIView
    from rest_framework.response import Response
    class TestView(APIView):
        authentication_classes = [TestAuthentication, ]                        #局部认证配置
        def get(self, request, *args, **kwargs):
            print(request.user)
            print(request.auth)
            return Response('GET请求,响应内容')
    
        def post(self, request, *args, **kwargs):
            return Response('POST请求,响应内容')
    
        def put(self, request, *args, **kwargs):
            return Response('PUT请求,响应内容')

    方式二:

    class MyViewAuth(object):
        authentication_classes = [TestAuthentication, ]
    
    from rest_framework.views import APIView
    from rest_framework.response import Response
    class TestView(MyViewAuth,APIView):                               #视图继承自定义MyViewAuth类,必须写在左边
        def get(self, request, *args, **kwargs):
            print(request.user)
            print(request.auth)
            return Response('GET请求,响应内容')
    
        def post(self, request, *args, **kwargs):
            return Response('POST请求,响应内容')
    
        def put(self, request, *args, **kwargs):
            return Response('PUT请求,响应内容')

    3、全局认证

      上述我们介绍了局部视图中配置认证的方式。但是如果我们需要对全局所有的视图进行认证,则需要在配置文件中进行配置来实现了。具体配置如下:

    REST_FRAMEWORK = {
        'UNAUTHENTICATED_USER': None,
        'UNAUTHENTICATED_TOKEN': None,
        "DEFAULT_AUTHENTICATION_CLASSES": [
            "app01.utils.TestAuthentication",                      #TestAuthentication文件中写自定义认证类
        ],
    
    }

    二、权限

    1、自定义权限

      权限即是通过继承BasePermission重构权限的类,权限的逻辑在类的has_permission方法中实现,返回True则有权限,返回False则没有权限,具体示例如下:

    from rest_framework.permissions import BasePermission
    class TestPermission(BasePermission):
        message = "权限验证失败"
        def has_permission(self, request, view):
            """
            判断是否有权限访问当前请求
            Return `True` if permission is granted, `False` otherwise.
            :param request: 
            :param view: 
            :return: True有权限;False无权限
            """
            if request.user == "管理员":
                return True
    
        # GenericAPIView中get_object时调用
        def has_object_permission(self, request, view, obj):
            """
            视图继承GenericAPIView,并在其中使用get_object时获取对象时,触发单独对象权限验证
            Return `True` if permission is granted, `False` otherwise.
            :param request: 
            :param view: 
            :param obj: 
            :return: True有权限;False无权限
            """
            if request.user == "管理员":
                return True

    2、局部使用

    class TestView(APIView):
        # 认证的动作是由request.user触发
        authentication_classes = [TestAuthentication, ]
        # 权限
        # 循环执行所有的权限
        permission_classes = [TestPermission, ]
    
        def get(self, request, *args, **kwargs):
            # self.dispatch
            print(request.user)
            print(request.auth)
            return Response('GET请求,响应内容')
    
        def post(self, request, *args, **kwargs):
            return Response('POST请求,响应内容')
    
        def put(self, request, *args, **kwargs):
            return Response('PUT请求,响应内容')

    3、全局使用

    REST_FRAMEWORK = {
        'UNAUTHENTICATED_USER': None,
        'UNAUTHENTICATED_TOKEN': None,
        "DEFAULT_AUTHENTICATION_CLASSES": [
            "app01.utils.TestAuthentication",
        ],
        "DEFAULT_PERMISSION_CLASSES": [
            "app01.utils.TestPermission",
        ],
    }

    三、频率

      具体实现方式见如下应用中的实例。全局使用限流的配置的如下:

    REST_FRAMEWORK = {
                        "DEFAULT_THROTTLE_CLASSES":[
                        ],   #限流类的路径,参考认证和权限
                        'DEFAULT_THROTTLE_RATES':{
                            'wdp_anon':'5/minute',
                            'wdp_user':'10/minute',
                        }
                    }

    四、应用实例

      有这么个应用要求,主页内容通过认证,根据认证结果登录用户可以在一分钟之内访问10次,匿名用户一分钟只能访问5次。对于订单页面通过认证、权限、设置访问频率,要求只有登录用户才能访问,同样一分钟内只能访问10次。

    1、视图代码

    from django.shortcuts import render,HttpResponse
    from django.http import JsonResponse
    from rest_framework import views
    from .models import *
    from .utils.auth import LuffyAuthentication
    from .utils.permission import LuffyPermission
    from .utils.throttle import LuffyAnonRateThrottle,LuffyUserRateThrottle
    
    ############以下是登录认证相关视图###########
    def gen_token(username):
        import time
        import hashlib
        ctime=str(time.time())
        hashcode=hashlib.md5(username.encode('utf-8'))
        hashcode.update(ctime.encode('utf-8'))
        return hashcode.hexdigest()
    
    
    class AuthView(views.APIView):
        def post(self,request,*args,**kwargs):
     
            ret={"code":1000,"msg":None}
            user=request.data.get("username")
            pwd=request.data.get("password")
            userobj=UserInfo.objects.filter(user=user,pwd=pwd).first()
            if userobj:
                tk=gen_token(user)
                Token.objects.update_or_create(user=userobj,defaults={'token':tk})
                ret["code"]=1001
                ret["token"]=tk
            else:
                ret["msg"]="用户名或者密码错误"
            return JsonResponse(ret)
    
    
    ############以下是首页视图###########
    class IndexView(views.APIView):
        """
           用户认证
               http://127.0.0.1:8001/v1/index/?tk=sdfasdfasdfasdfasdfasdf
               获取用户传入的Token
    
           首页限制:request.user
               匿名:5/m
               用户:10/m
           """
        authentication_classes = [LuffyAuthentication,]
        throttle_classes = [LuffyAnonRateThrottle,LuffyUserRateThrottle]
        def get(self,request,*args,**kwargs):
            return HttpResponse("首页")
    
    
    ############以下是订单页面视图###########
    class OrderView(views.APIView):
        """
            订单页面:只有登录成功后才能访问
                - 认证(匿名和用户)
                - 权限(用户)
                - 限制()
        """
        authentication_classes = [LuffyAuthentication,]
        permission_classes = [LuffyPermission,]
        throttle_classes = [LuffyUserRateThrottle,]
        def get(self, request, *args, **kwargs):
            return HttpResponse("订单页")

    2、url代码

    from django.conf.urls import url
    from django.contrib import admin
    from app01 import views
    
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^auth/', views.AuthView.as_view()),
        url(r'^index/', views.IndexView.as_view()),
        url(r'^order/', views.OrderView.as_view()),
    ]

    3、认证代码

    from rest_framework.authentication import BaseAuthentication
    from rest_framework import exceptions
    from app01 import models
    
    
    class LuffyAuthentication(BaseAuthentication):
        def authenticate(self, request):
            tk=request.query_params.get('tk')                        #拿取url上的token参数
            if not tk:
                return (None,None)
                # raise exceptions.AuthenticationFailed("认证失败")   #不允许有匿名用户情况
            tokenobj=models.Token.objects.filter(token=tk).first()
            if not tokenobj:
                return (None,None)
            return (tokenobj.user,tokenobj)

    4、权限代码

    from rest_framework.permissions import BasePermission
    
    class LuffyPermission(BasePermission):
        def has_permission(self, request, view):
            """
            Return `True` if permission is granted, `False` otherwise.
           """
            if request.user:
                return True
            return False

    5、限频代码

    from rest_framework.throttling import SimpleRateThrottle
    
    
    class LuffyAnonRateThrottle(SimpleRateThrottle):
        '''匿名用户以ip为唯一标识'''
    
        scope = "luffy_anon"
    
        def allow_request(self, request, view):
            """
            Return `True` if the request should be allowed, `False` otherwise.
            """
            if request.user:
                return True
    
            # 获取当前访问用户的唯一标识
            self.key = self.get_cache_key(request, view)
            # 根据当前用户的唯一标识,获取所有访问记录
            # [1511312683.7824545, 1511312682.7824545, 1511312681.7824545]
            self.history = self.cache.get(self.key, [])
            # 获取当前时间
            self.now = self.timer()
    
            # Drop any requests from the history which have now passed the
            # throttle duration
            while self.history and self.history[-1] <= self.now - self.duration:
                self.history.pop()
            if len(self.history) >= self.num_requests:
                return self.throttle_failure()
            return self.throttle_success()
    
        def get_cache_key(self, request, view):
            return 'throttle_%(scope)s_%(ident)s' % {
                'scope': self.scope,
                'ident': self.get_ident(request)
            }
    
    class LuffyUserRateThrottle(SimpleRateThrottle):
        '''登录用户以用户名为唯一标识'''
        scope = "luffy_user"
        def allow_request(self, request, view):
            """
            Return `True` if the request should be allowed, `False` otherwise.
            """
            if not request.user:
                return True
            # 获取当前访问用户的唯一标识
            # 用户对所有页面
            self.key = request.user.user
            # 用户对单页面
            # self.key = request.user.user + view.__class__.__name__
    
    
            # 根据当前用户的唯一标识,获取所有访问记录
            # [1511312683.7824545, 1511312682.7824545, 1511312681.7824545]
            self.history = self.cache.get(self.key, [])
            # 获取当前时间
            self.now = self.timer()
    
            # Drop any requests from the history which have now passed the
            # throttle duration
            while self.history and self.history[-1] <= self.now - self.duration:
                self.history.pop()
            if len(self.history) >= self.num_requests:
                return self.throttle_failure()
            return self.throttle_success()

    6、限频配置

    REST_FRAMEWORK = {
        'UNAUTHENTICATED_USER': None,
        'UNAUTHENTICATED_TOKEN': None,
        "DEFAULT_THROTTLE_RATES": {
            'luffy_anon': '5/m',                  #5次每分钟
            'luffy_user': '10/m'                  #10次每分钟
        },
    }
  • 相关阅读:
    防F12审查元素扒代码:按下F12关闭当前页面
    Wp-UserAgent——让WordPress在评论后面加上浏览器和操作系统信息
    WordPress中添加自定义评论表情包的方法
    WordPress彩色背景标签云实现
    让wordpress标签云显示文章数的正确方法
    如何在WordPress文本小工具中使用PHP
    WordPress非插件实现评论回复邮件提醒通知
    Firefox取消“订阅实时书签”功能
    WordPress修改标签云大小及颜色
    WordPress菜单“显示选项”无法显示的解决办法
  • 原文地址:https://www.cnblogs.com/seven-007/p/8469160.html
Copyright © 2020-2023  润新知