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次每分钟
    },
}

 

posted @ 2018-02-25 13:09  骑猪走秀  阅读(341)  评论(0编辑  收藏  举报