DRF 之 认证、权限、视图、频率组件

认证组件

只有认证通过的用户才能访问指定的url地址,比如:查询课程信息,需要登录之后才能查看,没有登录,就不能查看,这时候需要用到认证组件

表模型创建

model.py

class User(models.Model):
    username=models.CharField(max_length=32)
    password=models.CharField(max_length=32)
    user_type=models.IntegerField(choices=((1,'超级管理员'),(2,'普通管理员'),(3,'普通用户')))

class UserToken(models.Model):
    user=models.OneToOneField(to='User')
    token=models.CharField(max_length=64)

认证组件

新建 MyAuths.py 文件,在文件内定义一个认证类(文件名和类名随意)

from rest_framework.authentication import BaseAuthentication
class MyAuth(BaseAuthentication):
    def authenticate(self,request):
        #写一些认证的逻辑
        # print('我是认证类中的方法,只要配置了,一定会走我')
        token=request.GET.get('token')
        token_obj=models.Token.objects.filter(token=token).first()
        if token_obj:
            #有值表示登录了
            #token_obj.user 当前登录的user对象
            return token_obj.user,token_obj
        else:
            #没有值,表示没有登录,抛异常
            raise AuthenticationFailed('您没有登录')

局部使用

view.py 内的视图类中添加校验

# 用户必须登录之后才能访问获取所有图书接口
class Books(APIView):
    # 可以写多个认证类
    authentication_classes=[MyAuth,]
    def get(self,request):
        # request.user就是当前登录用户
        print(request.user.name)
        return Response('返回了所有图书')
        
class Login(APIView):
    authentication_classes = []
    def post(self,request):
        response={'code':100,'msg':'登录成功'}
        name=request.data.get('name')
        pwd=request.data.get('pwd')
        try:
            #get 有且只有一条才不报错,其他都抛异常
            user=models.User.objects.filter(name=name,pwd=pwd).get()
            #登录成功,需要去token表中存数据
            #生成一个唯一的idhg
            token=uuid.uuid4()
            models.Token.objects.update_or_create(user=user,defaults={'token':token})
            response['token']=token
        except ObjectDoesNotExist as e:
            response['code']=101
            response['msg']='用户名或密码错误'
        except Exception as e:
            response['code'] = 102
            response['msg'] = '未知错误'
        return Response(response)

总结:局部使用,只需要在视图类里加入:

authentication_classes = [TokenAuth, ]

全局使用

在settings文件中添加下面的参数

REST_FRAMEWORK={
    "DEFAULT_AUTHENTICATION_CLASSES":["app01.service.auth.Authentication",]
}

添加全局校验后回对所有的视图类都进行校验,如果有视图类不需要进行校验可以在视图类中把校验设置为空列表

# 用户不登录也可以访问获取所有出版社信息
class Publish(APIView):
    authentication_classes = []
    def get(self,request):
        print(request.user.name)
        return Response('返回了所有出版社信息')

认证类使用顺序:先用视图类中的验证类,再用settings里配置的验证类,最后用默认的验证类

权限组件

只用超级用户才能访问指定的数据,普通用户不能访问,所以就要有权限组件对其限制

权限组件

class MyPermision(BasePermission):
    message = '不是超级用户,查看不了'
    def has_permission(self,request,view):
        if request.user.user_type==1:
            return True
        else:
            return False

局部使用

view.py 内的视图类中添加校验

class Books(APIView):
    #只有超级用户才能访问该接口
    permission_classes=[MyPermision,]
    def get(self,request):
        print(request.user.name)
        return Response('返回了所有图书')

class Publish(APIView):
	# 任何有户都有权限进行访问
    permission_classes=[]
    def get(self,request):
        print(request.user.name)
        return Response('返回了所有出版社信息')

局部使用只需要在视图类里加入:

permission_classes = [UserPermission,]

全局使用

在settings文件中添加下面的参数

REST_FRAMEWORK={
    "DEFAULT_PERMISSION_CLASSES":["app01.service.permissions.SVIPPermission",]
}

权限类使用顺序:先用视图类中的权限类,再用settings里配置的权限类,最后用默认的权限类

视图组件

首先写一个序列组件

class PublishSerializers(serializers.ModelSerializer):
    class Meta:
        model=models.Publish
        fields='__all__'

基本视图

写一个出版社的增删查改resful接口

路由:

url(r'^publish/$', views.PublishView.as_view()),
url(r'^publish/(?P<pk>\d+)/$', views.PublishDetailView.as_view()),

视图:

class PublishView(APIView):
	# 获取所有的出版社信息
    def get(self, request):
        publish_list = models.Publish.objects.all()
        # 序列化数据
        bs = PublishSerializers(publish_list, many=True)
        return Response(bs.data)
	# 新增一条出版社信息
    def post(self, request):
        # 添加一条数据
        print(request.data)
        bs=PublishSerializers(data=request.data)
        if bs.is_valid():
            bs.save()  # 生成记录
            return Response(bs.data)
        else:
            return Response(bs.errors)

class PublishDetailView(APIView):
	# 获取一个出版社信息
    def get(self,request,pk):
        publish_obj=models.Publish.objects.filter(pk=pk).first()
        bs=PublishSerializers(publish_obj,many=False)
        return Response(bs.data)
    # 修改一个出版社信息    
    def put(self,request,pk):
        publish_obj = models.Publish.objects.filter(pk=pk).first()
        bs=PublishSerializers(data=request.data,instance=publish_obj)
        if bs.is_valid():
            bs.save() # update
            return Response(bs.data)
        else:
            return Response(bs.errors)
    # 删除一个出版社信息      
    def delete(self,request,pk):
        models.Publish.objects.filter(pk=pk).delete()
        return Response("")

mixin类和generice类编写视图

from rest_framework.mixins import CreateModelMixin,ListModelMixin,RetrieveModelMixin,DestroyModelMixin,UpdateModelMixin
from rest_framework.generics import GenericAPIView

class PublishView(CreateModelMixin,ListModelMixin,GenericAPIView):
    queryset = models.Publish.objects.all()
    serializer_class = PublishSerializers
    def post(self,request, *args, **kwargs):
        return self.create(request, *args, **kwargs)
    def get(self,request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

class PublishDetailView(RetrieveModelMixin,DestroyModelMixin,UpdateModelMixin,GenericAPIView):
    queryset = models.Publish.objects.all()
    serializer_class = PublishSerializers
    def get(self,request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)
    def put(self,request, *args, **kwargs):
        return self.update(request, *args, **kwargs)
    def delete(self,request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)

使用generics 下ListCreateAPIView,RetrieveUpdateDestroyAPIView

from rest_framework.generics import CreateAPIView,ListCreateAPIView,DestroyAPIView,RetrieveUpdateDestroyAPIView
class PublishView(ListCreateAPIView):
    queryset = models.Publish.objects.all()
    serializer_class = PublishSerializers

class PublishDetailView(RetrieveUpdateDestroyAPIView):
    queryset = models.Publish.objects.all()
    serializer_class = PublishSerializers

上面三种方法的路由都是一样的。

使用ModelViewSet

路由:

    url(r'^publish/$', views.PublishView.as_view({'get':'list','post':'create'})),
    url(r'^publish/(?P<pk>\d+)/$', views.PublishView.as_view({'get':'retrieve','put':'update','delete':'destroy'})),

视图:

from rest_framework.viewsets import ModelViewSet
class PublishView(ModelViewSet):
    queryset=models.Publish.objects.all()
    serializer_class=PublishSerializers

通这种方法可以实现通过同一个类根据请求的方式执行不同的方法:

路由:

url(r'^aa/$', views.PublishView.as_view({'get': 'aaa'})),

视图:

from rest_framework.viewsets import  ViewSetMixin
from rest_framework.views import  APIView
# ViewSetMixin 重写了as_view方法
class Test(ViewSetMixin,APIView):
    def aaa(self,request):
        return Response()

频率控制

为了控制用户对某个 url 请求的频率,比如,一分钟以内,只能访问三次

自定义频率类

1.取出访问者ip
2.判断当前ip不在访问字典里,添加进去,并且直接返回True,表示第一次访问,在字典里,继续往下走
3.循环判断当前ip的列表,有值,并且当前时间减去列表的最后一个时间大于60s,把这种数据pop掉,这样列表中只有60s以内的访问时间,
4.判断,当列表小于3,说明一分钟以内访问不足三次,把当前时间插入到列表第一个位置,返回True,顺利通过
5.当大于等于3,说明一分钟内访问超过三次,返回False验证失败
class MyThrottles():
    VISIT_RECORD = {}
    def __init__(self):
        self.history=None
    def allow_request(self,request, view):
        #(1)取出访问者ip
        # print(request.META)
        ip=request.META.get('REMOTE_ADDR')
        import time
        ctime=time.time()
        # (2)判断当前ip不在访问字典里,添加进去,并且直接返回True,表示第一次访问
        if ip not in self.VISIT_RECORD:
            self.VISIT_RECORD[ip]=[ctime,]
            return True
        self.history=self.VISIT_RECORD.get(ip)
        # (3)循环判断当前ip的列表,有值,并且当前时间减去列表的最后一个时间大于60s,把这种数据pop掉,这样列表中只有60s以内的访问时间,
        while self.history and ctime-self.history[-1]>60:
            self.history.pop()
        # (4)判断,当列表小于3,说明一分钟以内访问不足三次,把当前时间插入到列表第一个位置,返回True,顺利通过
        # (5)当大于等于3,说明一分钟内访问超过三次,返回False验证失败
        if len(self.history)<3:
            self.history.insert(0,ctime)
            return True
        else:
            return False
    def wait(self):
        import time
        ctime=time.time()
        return 60-(ctime-self.history[-1])

内置频率组件

写一个类,继承自SimpleRateThrottle

from rest_framework.throttling import SimpleRateThrottle
class VisitThrottle(SimpleRateThrottle):
    scope = 'throttle'
    def get_cache_key(self, request, view):
    	# 返回什么值,就以什么做过滤,返回用户id,就以用户id做过滤
        return self.get_ident(request)

在setting里配置:(一分钟访问三次)

REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_RATES':{
        'throttle':'3/m'
    }
}

在视图类里局部使用

throttle_classes = [MyThrottles,]

错误信息的中文提示:

class Course(APIView):
    authentication_classes = [TokenAuth, ]
    permission_classes = [UserPermission, ]
    throttle_classes = [MyThrottles,]
    def get(self, request):
        return HttpResponse('get')
    def post(self, request):
        return HttpResponse('post')
    def throttled(self, request, wait):
        from rest_framework.exceptions import Throttled
        class MyThrottled(Throttled):
            default_detail = '你的访问太频繁,请稍后访问!'
            extra_detail_singular = '还有 {wait} second.'
            extra_detail_plural = '出了 {wait} seconds.'
        raise MyThrottled(wait)
  • BaseThrottle是所有类的基类:方法:def get_ident(self, request) 获取标识,其实就是获取ip,自定义的需要继承它。
  • AnonRateThrottle:未登录用户ip限制,需要配合auth模块用
  • SimpleRateThrottle:重写此方法,可以实现频率现在,不需要咱们手写上面自定义的逻辑。
  • UserRateThrottle:登录用户频率限制,这个得配合auth模块来用。
  • ScopedRateThrottle:应用在局部视图上的。

全局使用

REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_CLASSES':['app01.utils.VisitThrottle',],
    'DEFAULT_THROTTLE_RATES':{
        'throttle':'3/m'
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值