三大认证之权限与频率
权限类使用
我们既然通过了登录校验那么就会继续走接下来的三大认证之一:权限,也就是说我们有一些接口只有管理员才能访问,普通用户访问
# 首先导入BasePermission然后写一个类继承BasePermission
from rest_framework.permissions import BasePermission
class RightPermission(BasePermission):
# message逐级查找message如果下面没有就会使用类中的message
message = '啊你不是vip请充值后访问'
# 重写has_permission方法
def has_permission(self, request, view):
print(request.user.user_type.name)
if request.user.user_type.name == '超级管理员': # 验证是否为超级管理员
return True # 如果在用户类型表中是超级管理员那么有资格访问,返回True
else: # 否则返回False
#我们也可以给他返回自定义提示信息,如果函数中没有则去类中寻找
# 如果我们使用的是choice的情况下,可以使用user.user_type 拿到的数字类型,想变成字符串的形式那么我们可以使用user.get_user_type_display()
self.message = '您是: %s ,您没有权限访问该程序'%request.user.user_type.name
return False
# 最后去使用
频率类使用
# 首先我们导入频率类的配置,写一个类用来继承,一共有两种继承方式,一种是BaseThrottle
# 第二种则是SimpleRateThrottle,第一种需要改写allow_request,而第二种只需要改写get_cache_key
# 我们暂时先使用SimpleRateThrottle
from rest_framework.throttling import BaseThrottle,SimpleRateThrottle
# 我们继承SimpleRateThrottle
class Frequency(SimpleRateThrottle):
# 类属性,这个类属性我们可以随便进行命名,但是需要跟配置文件相对应
scope = 'pig'
# 改写get_cache_key
def get_cache_key(self, request, view):
# 返回什么频率就会以什么做限制
# 返回ip地址来做限制
return request.META.get('REMOTE_ADDR')
# 写入全局配置
REST_FRAMEWORK={
# 给全局使用,我们可以使用局部禁用来禁止频率
'DEFAULT_THROTTLE_CLASSES':['app01.frequency.Frequency'],
# 定义访问频率
'DEFAULT_THROTTLE_RATES':{
'pig':'5/m' # 每分钟只能访问5次, 5/h 每小时只能访问5次, 5/s 每秒只能访问5次, 5/d 每天只能访问5次
}
}
一个标准的Python 字典,包含所有的HTTP 首部。具体的头部信息取决于客户端和服务器,下面是一些示例:
取值:
CONTENT_LENGTH —— 请求的正文的长度(是一个字符串)。
CONTENT_TYPE —— 请求的正文的MIME 类型。
HTTP_ACCEPT —— 响应可接收的Content-Type。
HTTP_ACCEPT_ENCODING —— 响应可接收的编码。
HTTP_ACCEPT_LANGUAGE —— 响应可接收的语言。
HTTP_HOST —— 客服端发送的HTTP Host 头部。
HTTP_REFERER —— Referring 页面。
HTTP_USER_AGENT —— 客户端的user-agent 字符串。
QUERY_STRING —— 单个字符串形式的查询字符串(未解析过的形式)。
REMOTE_ADDR —— 客户端的IP 地址。
REMOTE_HOST —— 客户端的主机名。
REMOTE_USER —— 服务器认证后的用户。
REQUEST_METHOD —— 一个字符串,例如"GET" 或"POST"。
SERVER_NAME —— 服务器的主机名。
SERVER_PORT —— 服务器的端口(是一个字符串)。
从上面可以看到,除 CONTENT_LENGTH 和 CONTENT_TYPE 之外,请求中的任何 HTTP 首部转换为 META 的键时,
都会将所有字母大写并将连接符替换为下划线最后加上 HTTP_ 前缀。
所以,一个叫做 X-Bender 的头部将转换成 META 中的 HTTP_X_BENDER 键。
认证源码分析
首先我们因为都是继承了APIVIEW的执行流程,并且包装了新的request对象,并且执行了三大认证,执行视图类的方法,处理了全局异常
首先我们在APIView中找到dispatch,进入
def dispatch(self, request, *args, **kwargs): #
request = self.initialize_request(request, *args, **kwargs)
在initial的内部有三个功能,也就是我们的三大认证:认证,权限,频率,我们需要该写的也在这三个内部
def initial(self, request, *args, **kwargs):
self.perform_authentication(request) # 认证
self.check_permissions(request) # 权限
self.check_throttles(request) # 频率
认证源码,就一句话返回了request.user 这里的request是新的request,User属性则是在Request类中,所以我们应该到Request中取寻找
def perform_authentication(self, request):
request.user
来到Request中寻找
@property # 将user属性伪装成一个方法
def user(self):
if not hasattr(self, '_user'): # Request类的对象中反射的_user
with wrap_attributeerrors():
self._authenticate() # 如果是第一次的话那么就一定会走这个代码
return self._user
我们顺着_authenticate寻找
def _authenticate(self):
for authenticator in self.authenticators:
try:
# (user_token.user,token)
user_auth_tuple = authenticator.authenticate(self) # 调用认证类对象的authenticator
except exceptions.APIException:
self._not_authenticated()
raise
if user_auth_tuple is not None:
self._authenticator = authenticator
self.user, self.auth = user_auth_tuple # 解压赋值
return
# 认证类可以配置多个,但是只要其中有一个反悔了两个值,那么接下来的所有认证类都不会被执行
self._not_authenticated()
# 认证类,要重写authenticate方法,我们认证后通过返回两个值或翻译一个None,那么认证不通过就抛AuthenticationFailed(继承了APIException)异常
权限源码分析
首先我们因为都是继承了APIVIEW的执行流程,并且包装了新的request对象,并且执行了三大认证,执行视图类的方法,处理了全局异常
首先我们在APIView中找到dispatch,进入
def dispatch(self, request, *args, **kwargs): #
request = self.initialize_request(request, *args, **kwargs)
在initial的内部有三个功能,也就是我们的三大认证:认证,权限,频率,我们需要该写的也在这三个内部
def initial(self, request, *args, **kwargs):
self.perform_authentication(request) # 认证
self.check_permissions(request) # 权限
self.check_throttles(request) # 频率
权限源码
def check_permissions(self, request):
for permission in self.get_permissions():
# permission是配置在视图类中得权限类的对象,对象调用它的绑定方法has_permission,对象调用自己的绑定方法会把自己传入(权限类的对象,request,视图类的对象)
if not permission.has_permission(request, self):
self.permission_denied(
request,
message=getattr(permission, 'message', None),
code=getattr(permission, 'code', None)
)
进入self.get_permissions
def get_permissions(self):
return [permission() for permission in self.permission_classes]
self.permission_classes 视图类中配的权限类列表
permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES 返回的是在视图类中配的权限类的对象列表[UserTypePermession(),]
频率源码分析
首先我们因为都是继承了APIVIEW的执行流程,并且包装了新的request对象,并且执行了三大认证,执行视图类的方法,处理了全局异常
首先我们在APIView中找到dispatch,进入
def dispatch(self, request, *args, **kwargs): #
request = self.initialize_request(request, *args, **kwargs)
在initial的内部有三个功能,也就是我们的三大认证:认证,权限,频率,我们需要该写的也在这三个内部
def initial(self, request, *args, **kwargs):
self.perform_authentication(request) # 认证
self.check_permissions(request) # 权限
self.check_throttles(request) # 频率
权限源码
def check_throttles(self, request):
throttle_durations = []
for throttle in self.get_throttles(): # 还是一个循环
if not throttle.allow_request(request, self):
throttle_durations.append(throttle.wait()) # 如果我们要写频率类那么就必须重写allow_request方法,返回True(没有频率的限制)返回False(到达频率限制峰值)
鸭子类型
我们之前学的鸭子类型其实就是一句跟垃圾的话,就是走路像鸭子,说话像鸭子,那么他就是只鸭子,太过笼统和片面,硬说的话也不算错,但是并不不完满
其实鸭子类型指的是面向对象中,子类不需要显示的继承某个类,只要某个的方法和属性,的方法和属性,那么就属于这个类
# 假设有个鸭子类Duck类,有两个方法,run,speak方法
# 假设又有一个普通鸭子类,PDuck,如果它也是鸭子,它需要继承Duck类,只要继承了鸭子类,什么都不需要写,普通鸭子类的对象就是鸭子这种类型;如果不继承,普通鸭子类的对象就不是鸭子这种类型
#假设又有一个唐老鸭子类,TDuck,如果它也是鸭子,它需要继承Duck类,只要继承了鸭子类,什么都不需要写,唐老鸭子类的对象就是鸭子这种类型;如果不继承,唐老鸭子类的对象就不是鸭子这种类型
# python不推崇这个,它推崇鸭子类型,指的是
不需要显示的继承某个类,只要我的类中有run和speak方法,我就是鸭子这个类
# 有小问题:如果使用python鸭子类型的写法,如果方法写错了,它就不是这个类型了,会有问题
# python为了解决这个问题:
-方式一:abc模块,装饰后,必须重写方法,不重写就报错
-方式二:drf源码中使用的:父类中写这个方法,但没有具体实现,直接抛异常
练习1
perims.py
from rest_framework.permissions import BasePermission
class RightPermission(BasePermission):
message = '啊你不是vip请充值后访问'
def has_permission(self, request, view):
print(request.user.user_type.name)
if request.user.user_type.name == '超级管理员':
return True
else:
self.message = '您是: %s ,您没有权限访问该程序'%request.user.user_type.name
return False
views.py
class BookView1(GenericAPIView, ListModelMixin):
authentication_classes = [LoginAuth, ]
permission_classes = [RightPermission, ]
throttle_classes = [Frequency,]
queryset = Book.objects.all()
serializer_class = BookSerializer
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
class BookView2(GenericAPIView, CreateModelMixin):
authentication_classes = [LoginAuth, ]
permission_classes = [RightPermission, ]
throttle_classes = [Frequency,]
queryset = Book.objects.all()
serializer_class = BookSerializer
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
class BookDetailView1(GenericAPIView, RetrieveModelMixin):
authentication_classes = [LoginAuth, ]
permission_classes = [RightPermission, ]
throttle_classes = [Frequency, ]
queryset = Book.objects.all()
serializer_class = BookSerializer
def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs)
class BookDetailView2(GenericAPIView, UpdateModelMixin):
authentication_classes = [LoginAuth, ]
permission_classes = [RightPermission, ]
throttle_classes = [Frequency, ]
queryset = Book.objects.all()
serializer_class = BookSerializer
def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
class BookDetailView(GenericAPIView, DestroyModelMixin):
authentication_classes = [LoginAuth, ]
permission_classes = [RightPermission, ]
throttle_classes = [Frequency, ]
queryset = Book.objects.all()
serializer_class = BookSerializer
def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs)
class PublishView1(GenericAPIView, ListModelMixin):
# authentication_classes = []
permission_classes = []
throttle_classes = [Frequency, ]
queryset = Publish.objects.all()
serializer_class = PublishSerializer
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
class PublishView2(GenericAPIView, ListModelMixin, CreateModelMixin):
# authentication_classes = []
permission_classes = []
throttle_classes = [Frequency, ]
queryset = Publish.objects.all()
serializer_class = PublishSerializer
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
class PublishDetailView1(GenericAPIView, UpdateModelMixin):
# authentication_classes = []
permission_classes = []
throttle_classes = [Frequency, ]
queryset = Publish.objects.all()
serializer_class = PublishSerializer
def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
class PublishDetailView2(GenericAPIView, RetrieveModelMixin):
# authentication_classes = []
permission_classes = []
throttle_classes = [Frequency, ]
queryset = Publish.objects.all()
serializer_class = PublishSerializer
def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs)
class PublishDetailView(GenericAPIView, DestroyModelMixin):
# authentication_classes = []
permission_classes = []
throttle_classes = [Frequency, ]
queryset = Publish.objects.all()
serializer_class = PublishSerializer
def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs)
settings.py
REST_FRAMEWORK={
'DEFAULT_THROTTLE_CLASSES':['app01.frequency.Frequency'],
'DEFAULT_THROTTLE_RATES':{
'pig':'5/m' # 每分钟只能访问5次, 5/h 每小时只能访问5次, 5/s 每秒只能访问5次, 5/d 每天只能访问5次
}
}
练习2
from rest_framework.permissions import BasePermission
class RightPermission(BasePermission):
message = '啊你不是vip请充值后访问'
# 重写has_permission方法
def has_permission(self, request, view):
print(request.user.user_type.name)
if request.user.user_type.name == '超级管理员':
return True
else:
self.message = '您是: %s ,您没有权限访问该程序'%request.user.user_type.name
return False
练习3
1.认证:
1.入口APIView中的dispatch方法中执行了三大认证
2.执行perform_authentication方法
3.执行request.user:user方法(通过@property装饰器伪装了属性)第一次走_authenticate方法
4.执行self._authenticate()
for循环self.authenticators-配置在视图类中的所有认证对象
(authenticators=None,是在新的Request中传入的参数)
调用认证类的对象
将返回的两个值解压赋值self.user, self.auth = user_auth_tuple
5.所以写认证类继承BaseAuthentication需要重写authenticate方法
6.拿到用户提交的有带认证的堆积字符串与数据库数据判断后 返回对应数据
成功会返回两个值(token_user.user, token) 之后就可以使用登录用户对象数据