drf权限认证
一 认证Authentication
1.1 自定义认证方案
认证的实现
1 写一个类,继承BaseAuthentication,重写authenticate,认证的逻辑写在里面,认证通过,返回两个值,一个值最终给了Requet对象的user,认证失败,抛异常:APIException或者AuthenticationFailed
2 全局使用,局部使用
案例:
登陆接口,查询图书接口,必须登录后才能查看,token信息放在头里(认证组件),全局使用,局部禁用(login禁用)
使用simplerouter自动生成路由
流程图

完整自己编写:
1 #url 2 from django.contrib import admin 3 from django.urls import path 4 from api import views 5 from rest_framework import routers 6 res=routers.SimpleRouter() 7 res.register('book',views.Bookview) 记得注册 8 9 urlpatterns = [ 10 path('admin/', admin.site.urls), 11 path('login/',views.Login.as_view()) 12 ] 13 urlpatterns+=res.urls 14 15 #view 16 from rest_framework.response import Response 17 from rest_framework.viewsets import ModelViewSet 18 from rest_framework.views import APIView 19 from api.ser import Myser 20 import uuid 21 from api import models 22 # Create your views here. 23 class Login(APIView): 24 authentication_classes = [] #不要放进请求内 是放在外面的 25 def post(self,request): 26 27 username=request.data.get('username') 28 password=request.data.get('password') 29 #读取用户 看清楚类 30 user=models.User.objects.filter(username=username,password=password).first() 31 32 if user: 33 token=uuid.uuid4() 34 #看清楚类是token类 # 有用户更新无用户创建并更新 判断有无 35 models.UserToken.objects.update_or_create(defaults={'token':token},user=user) 36 return Response({'status': 100, 'msg': '登陆成功', 'token': token}) 37 else: 38 return Response({'status': 101, 'msg': '登陆失败'}) 39 40 class Bookview(ModelViewSet): 41 42 queryset = models.Book.objects.all() #获取需要校验的全部数据 43 serializer_class = Myser 44 45 #认证模块 46 from rest_framework.authentication import BaseAuthentication 47 from rest_framework.exceptions import AuthenticationFailed 48 from api import models 49 from rest_framework.exceptions import ValidationError 50 51 class MyAuthentication(BaseAuthentication): 52 def authenticate(self,request): 53 token=request.GET.get('token') 54 if token: 55 user_token=models.UserToken.objects.filter(token=token).first() 56 if user_token: 57 return user_token.user,token 58 else: 59 raise AuthenticationFailed('认证失败') 60 else: 61 raise AuthenticationFailed('需要带token') 62 #序列化模块 63 64 from rest_framework import serializers 65 from api.models import Book 66 67 class Myser(serializers.ModelSerializer): 68 class Meta: 69 model=Book 70 fields='__all__' 71 72 #models模型层 73 from django.db import models 74 75 # Create your models here. 76 # 用户用户认证 77 class User(models.Model): 78 username=models.CharField(max_length=32) 79 password=models.CharField(max_length=32) 80 user_type=models.IntegerField(choices=((1,'超级用户'),(2,'普通用户'),(3,'其他用户'))) 81 82 class UserToken(models.Model): 83 user=models.OneToOneField(to='User',on_delete=models.CASCADE) 84 token=models.CharField(max_length=64) 85 86 87 class Book(models.Model): 88 name=models.CharField(max_length=32) 89 price=models.CharField(max_length=32)
1.1.1 编写models
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)
1.1.2 新建认证类
在app下新建一个py文件
from rest_framework.authentication import BaseAuthentication from rest_framework.exceptions import AuthenticationFailed from app01.models import UserToken class MyAuthentication(BaseAuthentication): def authenticate(self, request): token = request.META.get('HTTP_TOKEN') 会自动加前缀HTTP_TOKEN要注意(否则拿不出来 头/?token=‘**’) if token: user_token = UserToken.objects.filter(token=token).first() if user_token: return user_token.user, token #返回两个值是不需要任何包裹的(成功返回user,auth后面就可以reuqest取值了) else: raise AuthenticationFailed('认证失败') else: raise AuthenticationFailed('请求头中需要携带token')
1.1.3 编写视图
from rest_framework.decorators import action from app01 import models from rest_framework.views import APIView import uuid from app01.ser import BookSerializers from rest_framework.viewsets import ModelViewSet from rest_framework.response import Response class Login(APIView): authentication_classes = []#意思是局部禁用 不要放在请求内 def post(self, request): username = request.data.get('username') password = request.data.get('password') user = models.users.objects.filter(username=username, password=password).first() if user: token = uuid.uuid4() #有用户更新无用户创建并更新 判断 models.UserToken.objects.update_or_create(defaults={'token': token}, user=user) return Response({'status': 100, 'msg': '登陆成功', 'token': token}) else: return Response({'status': 101, 'msg': '登陆失败'}) # 使用ModelViewSet编写5个接口 class Bookview(ModelViewSet): queryset = models.book.objects.all() #序列化器和被序列的数据相同 serializer_class = BookSerializers @action(methods=['GET', 'POST'], detail=True) def get_10(self,request,pk): book = self.get_queryset()[:10] # 从0开始截取一条 ser = self.get_serializer(book, many=True) return Response(ser.data)
1.1.4 编写序列化器
from rest_framework import serializers from app01.models import book class BookSerializers(serializers.ModelSerializer): class Meta: model = book fields = '__all__'
1.1.5 全局使用
REST_FRAMEWORK={ "DEFAULT_AUTHENTICATION_CLASSES":["app01.app_auth.MyAuthentication",] }
1.1.6 局部使用
#局部使用,只需要在视图类里加入: authentication_classes = [MyAuthentication]
1.2 认证的源码分析
在APIView下的dispath方法中有request = self.initialize_request(request, *args, **kwargs),这句代码重写了request,并在request中加入了authenticators=self.get_authenticators()。让我们来分析一下这段代码:
def get_authenticators(self): """ Instantiates and returns the list of authenticators that this view can use. """ return [auth() for auth in self.authentication_classes]
get_authenticators()只有简单的那么一句代码,这句代码是一个标准列表生成式,他的意思是遍历self.authentication_classes加括号执行,并存放到列表当中。那么self.authentication_classes里面又有什么东西呢?
点进去我们看见是authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES,他的意思是从配置文件 中找到取出值赋值给authentication_classes这个列表,而这个列表中的值就是认证方法。默认的认证方法是无效的,因此我们要自己定义个authentication_classes并在里面写入自己定义的认证方法。
在drf中取数据的顺序是先从自己定义的类中查找,找不到再去项目下的settings.py文件中查找,最后再去框架中的配置文件中查找。知道了这个后就能很清楚的理解为何要自己定义authentication_classes和全局使用,局部使用,局部禁用,全局禁用的原理了。举个例子:上面views.py的代码中我写了authentication_classes = [],根据类方法的自调用[auth() for auth in self.authentication_classes]中的self.authentication_classes指的就是我自己写的空列表,列表为空,生成式也为空,那么到认证代码也是空,这就达到了局部禁用的效果。(个人觉得这里最绕的还是类的自调用啥的,所以还是再去好好学下类吧….)
回归主题知道了authentication_classes中放的是你写的认证类之后,那[auth() for auth in self.authentication_classes]这代码的执行结果应该是认证类的对象列表,递推回去那么authenticators=self.get_authenticators()中authenticators就是认证类的对象列表,经过绕来绕去最后到了request对象下的_authenticate中。
-request.py下的 def _authenticate(self): # 遍历拿到一个个认证器,进行认证 #self.authenticators 你在视图类中配置的一个个的认证类:authentication_classes=[认证类1,认证类2],对象的列表 for authenticator in self.authenticators: try: # 认证器(对象)调用认证方法authenticate(认证类对象self, request请求对象) # 返回值:登陆的用户与认证的信息组成的 tuple # 该方法被try包裹,代表该方法会抛异常,抛异常就代表认证失败 user_auth_tuple = authenticator.authenticate(self) #注意这self是request对象 except exceptions.APIException: self._not_authenticated() raise # 返回值的处理 if user_auth_tuple is not None: self._authenticator = authenticator # 如何有返回值,就将 登陆用户 与 登陆认证 分别保存到 request.user、request.auth self.user, self.auth = user_auth_tuple return # 如果返回值user_auth_tuple为空,代表认证通过,但是没有 登陆用户 与 登陆认证信息,代表游客 self._not_authenticated()
上面这段代码就是核心代码了
二 权限Permissions
权限控制可以限制用户对于视图的访问和对于具体数据对象的访问。
- 在执行视图的dispatch()方法前,会先进行视图访问权限的判断
- 在通过get_object()获取具体对象时,会进行模型对象访问权限的判断
2.1 自定义权限
2.1.1 编写权限类
写一个类,继承BasePermission,重写has_permission,如果权限通过,就返回True,不通过就返回False
class MyPermission(BasePermission): def has_permission(self, request, view): # 不是超级用户,不能访问 # 由于认证已经过了,request内就有user对象了,当前登录用户 user = request.user # 如果该字段用了choice,通过get_字段名_display()就能取出choice后面的中文 # print(user.get_user_type_display()) if user.user_type==1: return True else: return False
2.1.2 全局使用
REST_FRAMEWORK={ # 认证类的全局使用 "DEFAULT_AUTHENTICATION_CLASSES":["app01.app_auth.MyAuthentication",], # 权限类的全局使用 'DEFAULT_PRESSION_CLASSES':['app01.app_auth.MyPermission,'] }
2.1.3 局部使用和禁用
# 局部使用只需要在视图类里加入: permission_classes = [MyPermission,] # 局部禁用只需要在视图类里加入: permission_classes = []
2.1.4 说明
如需自定义权限,需继承rest_framework.permissions.BasePermission父类,并实现以下两个任何一个方法或全部
has_permission(self, request, view)是否可以访问视图, view表示当前视图对象
has_object_permission(self, request, view, obj)是否可以访问数据对象, view表示当前视图, obj为数据对象
2.2 权限源码分析
APIView—->dispatch—->initial—>self.check_permissions(request)(APIView的对象方法)通过上述的一层层查找我们能找到权限的核心源码
def check_permissions(self, request): """ Check if the request should be permitted. Raises an appropriate exception if the request is not permitted. """ for permission in self.get_permissions(): if not permission.has_permission(request, self): self.permission_denied( request, message=getattr(permission, 'message', None) )
权限的核心代码很多地方其实和认证的差不多。self.get_permissions()点进去后会发现他的源码和认证的几乎一样,这就代表着他存放的是权限类的对象集合成的列表。
遍历一个个权限列表得到一个个权限对象(权限器),进行权限认证。
权限类一定有一个has_permission权限方法,用来做权限认证的。
参数:权限对象self、请求对象request、视图类对象
返回值:有权限返回True,无权限返回False 如果返回值不是True就代表他没权限进行限制访问
2.3 内置权限(了解)(django的admin)
演示一下内置权限的使用:IsAdminUser,控制是否对网站后台有权限的人
# 1 创建超级管理员 # 2 写一个测试视图类 from rest_framework.permissions import IsAdminUser from rest_framework.authentication import SessionAuthentication //配置内置权限认证模块一起使用(没通过认证的是无法进入权限校验) class TestView3(APIView): authentication_classes=[SessionAuthentication,] permission_classes = [IsAdminUser] def get(self,request,*args,**kwargs): return Response('这是22222222测试数据,超级管理员可以看') # 3 超级用户登录到admin,再访问test3就有权限 # 4 正常的话,普通管理员,没有权限看(判断的是is_staff字段)
三 频率(限流)
可以对接口访问的频次进行限制,以减轻服务器压力。
一般用于付费购买次数,投票等场景使用.
3.1 内置频率类(就是对django的admin起作用)
3.1.1 AnonRateThrottle(from rest_framework.throttling import AnonRateThrottle)
限制所有匿名未认证用户,使用IP区分用户。
使用DEFAULT_THROTTLE_RATES['anon'] 来设置频次
-views.py from rest_framework.permissions import IsAdminUser from rest_framework.authentication import BasicAuthentication class TestView4(APIView): authentication_classes=[] permission_classes = [] def get(self,request,*args,**kwargs): return Response('我是未登录用户') # 全局使用 -settings.py REST_FRAMEWORK = { 'DEFAULT_THROTTLE_CLASSES': ( 'rest_framework.throttling.AnonRateThrottle', ), 'DEFAULT_THROTTLE_RATES': { 'anon': '3/m',# 使用 `second`, `minute`, `hour` 或`day`来指明周期。 } } # 局部使用 -views.py class TestView4(APIView): ... throttle_classes = [AnonRateThrottle] def get(self,request,*args,**kwargs): return Response('我是未登录用户')
3.1.2 UserRateThrottle(from rest_framework.throttling import UserRateThrottle)
限制认证用户,使用User id 来区分。
使用DEFAULT_THROTTLE_RATES['user'] 来设置频次
from rest_framework.throttling import UserRateThrottle class test4(APIView): 内置(admin) authentication_classes = [SessionAuthentication] 需要使用内置登录后才会生效 不然被会执行未登录的效果 内置
throttle_classes = [UserRateThrottle] def get(self,request,*args,**kwargs): return Response('我是未登录用户')
全局:在setting中 'DEFAULT_THROTTLE_CLASSES': ( 'rest_framework.throttling.UserRateThrottle' ), 'DEFAULT_THROTTLE_RATES': { 'user': '10/m', } 局部配置: 在视图类中配一个就行
3.1.3 ScopedRateThrottle
限制用户对于每个视图的访问频次,使用ip或user id。
class ContactListView(APIView): throttle_scope = 'contacts' ... class ContactDetailView(APIView): throttle_scope = 'contacts' ... class UploadView(APIView): throttle_scope = 'uploads' ... REST_FRAMEWORK = { 'DEFAULT_THROTTLE_CLASSES': ( 'rest_framework.throttling.ScopedRateThrottle', ), 'DEFAULT_THROTTLE_RATES': { 'contacts': '1000/day', 'uploads': '20/day' } }
3.2 自定义根据IP限制
写一个类,继承SimpleRateThrottle,只需要重写get_cache_key
from rest_framework.throttling import ScopedRateThrottle,SimpleRateThrottle #继承SimpleRateThrottle class MyThrottle(SimpleRateThrottle): scope='luffy' def get_cache_key(self, request, view): return request.META.get('REMOTE_ADDR') # 返回 # 局部使用,全局使用 REST_FRAMEWORK={ 'DEFAULT_THROTTLE_CLASSES': ( 'utils.throttling.MyThrottle', ), 'DEFAULT_THROTTLE_RATES': { 'luffy': '3/m' # key要跟类中的scop对应 }, }

浙公网安备 33010602011771号