DRF 5 认证权限频率含源码详解
API视图类提供了认证,权限,频率限制的功能,使用方法很简单,在框架内对应的地方写业务逻辑即可.
5.1 认证
简单来说就是:认证就是确定你是谁,认证的方法有很多,
基于密码认证:每次登陆都要输入账号密码,对用户不友好
基于session原理认证:
第一次登陆后服务器给你一个随机字符串,你的浏览器保存下来(这叫cookie),我服务器也保存一份同时还包含于用户的对应关系(这叫session),下次再来浏览器自动携带者随机字符串,服务器用该字符串在session表里查找,查到结果就能确定你是谁了.但是当用户量很大的时候存储session表可是个大麻烦.
基于token原理认证:
和session类似,也是给你一个字符串,但不是随机的,而是用你的信息加密后的字符串,服务器拿到字符串后只需要反解信息就能获得你的信息,从而确定你是谁,服务器再也花费大量资金存session了,岂不美哉.
JWT认证:json web token是token认证的一种实现形式,在第7节详细介绍
认证功能的使用
-
写一个类继承BaseAuthentication,重写authenticate方法.认证通过返回user对象,否则抛一个异常
-
在需要认证的视图类里配置 authentication_classes=[认证类1,认证类2...]
drf还提供了配合Django的user表使用的认证权限类(登录认证权限需要配套使用),这里不再展开
全局使用,在setting.py中配置
REST_FRAMEWORK={
"DEFAULT_AUTHENTICATION_CLASSES":["app01.app_auth.MyAuthentication",]
}
局部使用,写在视图类里,可以有多个认证,从左到右依次执行
authentication_classes=[MyAuthentication]
局部禁用
authentication_classes=[]
使用举例:使用session原理实现认证
# 写一个认证类 app_auth.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):
# 认证逻辑,如果认证通过,返回两个值
#如果认证失败,抛出AuthenticationFailed异常
#虽然变量名叫token但实现原理是session原理,因为服务端存了随机字符串与用户关系表
token=request.GET.get('token')
if token:
#如果用户携带了token直接以token为过滤条件筛选,有结果token就是对了
#这样通过token这个对象也能找到用户对象了
user_token=UserToken.objects.filter(token=token).first()
# 认证通过
if user_token:
return user_token.user,token
#链表查询返回user对象,反向查询表名小写直接获得表对象
else:
raise AuthenticationFailed('认证失败')
else:
raise AuthenticationFailed('请求地址中需要携带token')
实现原理
源码分析:
认证代码执行路径:views-->APIView-->as_view-->dispatch-->self.initial-->self.perform_authentication-->request.user
在此之前代码都在APIView中运行,执行了request.user之后,转到Request类
request.user -->self._authenticate方法
核心源码:
Request类中:
def _authenticate(self):
for authenticator in self.authenticators:
try:
user_auth_tuple = authenticator.authenticate(self)
#调用认证类对象的authenticate方法,该方法返回一个元祖或一个异常
except exceptions.APIException:
self._not_authenticated()
raise
if user_auth_tuple is not None:
self._authenticator = authenticator
#认证通过,将user,auth属性赋值给request对象
self.user, self.auth = user_auth_tuple
return
# self.authenticators的合理性
源码解析
#1,self.authenticators的authenticators从哪来的,它是什么?
#当前代码在Request类中,所以self代表request对象,为什么request对象有authenticators属性呢?
#authenticators是APIView在视图类获取配置的认证类之后调用生成的认证类对象,该属性在APIView通过initialize_request实例化request对象时赋值的,所以request对象具有authenticators属性.
APIView
def initialize_request(self, request, *args, **kwargs):
parser_context = self.get_parser_context(request)
return Request(
request,
parsers=self.get_parsers(),
authenticators=self.get_authenticators(),
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)
#该方法的 self.get_authenticators
def get_authenticators(self):
return [auth() for auth in self.authentication_classes]
#2,重写的authenticate方法需要返回什么结果?
认证类的authenticate方法,认证通过返回一个元祖包含两个值,认证失败抛一个异常
认证通过:
self.user, self.auth = user_auth_tuple 返回的第一个值会赋值个self.user,当前在Request类里,所以这两个值都给了request对象,即以后的request就有了这两个属性
认证失败:
from rest_framework.exceptions import APIException,AuthenticationFailed
抛一个AuthenticationFailed异常会被捕获
5.2 权限
权限需要和认证配合使用
确定了你是谁之后再检查你的权限,权限不够就不提供服务
自定义权限组件的使用
1)写一个类,继承BasePermission,重写has_permission,如果权限通过,就返回True,不通过就返回False
- 使用, 在需要认证的视图类里配置 permission_classes =[权限类1,权限类2...]
局部使用
class TestView(APIView):
permission_classes = [app_auth.UserPermission]
全局使用
REST_FRAMEWORK={
"DEFAULT_AUTHENTICATION_CLASSES":["app01.app_auth.MyAuthentication",],
'DEFAULT_PERMISSION_CLASSES': ['app01.app_auth.UserPermission',],
}
局部禁用
class TestView(APIView):
permission_classes = []
from rest_framework.permissions import BasePermission
class UserPermission(BasePermission):
#重写has_permission,如果权限通过,就返回True,不通过就返回False
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
实现原理
源码路径分析:
rest_framework-->views-->APIView-->as_view-->dispatch-->self.initial-->self.check_permissions 一直在APIView这个视图类里
核心源码:
def check_permissions(self, request):
for permission in self.get_permissions(): #一个个由权限类组成的列表被调用生成对象
if not permission.has_permission(request, self):#调用对象has_permission,获取结果
self.permission_denied(
request, message=getattr(permission, 'message', None)
)
源码解析:
# 1)为什么认证需要到Request类中调用方法,权限不需?
认证去Request调用方法是为了控制APIView的request能否有user这个对象属性以判断用户是否登录, 若用户登录则request对象就有了user对象属性,权限判断在认证之后执行,若用户登录了,request就有 user对象属性了,所以不用去Request类中调用方法.
# 2) self.get_permissions是什么?
和认证的authenticator类似,get_permissions得到一个个由权限类组成的列表
# 3)重写的has_permission需要返回什么结果?
学习一下源码的返回写法:
def has_permission(self, request, view):
return bool(request.user and request.user.is_staff)
布尔值,有权限就返回True,否则返回False
只使用user表配合自定义认证,权限类
Django的user表很好用,配合Django内置的登录认证权限效果更佳,但是对于复杂的业务逻辑需求,我们可以只使用user表.
认证,权限类还是自己定义,具体使用user表的时候需要使用user表提供的方法,user常用方法如下:
# 1.比对用户名和密码是否正确
user_obj = auth.authenticate(request,username=username,password=password)
# 2.保存用户状态
auth.login(request,user_obj)
# 类似于request.session[key] = user_obj
# 3.判断当前用户是否登陆,返回布尔值
request.user.is_authenticated()
# 4.获取当前登陆用户
request.user
# 5.比对原密码,返回布尔值
request.user.check_password(old_password)
# 6.修改密码
request.user.set_password(new_password) # 修改对象中的password属性
request.user.save() # 操作数据库保存修改后的对象
# 7.注销
auth.logout(request)
# 8.注册
# 操作auth_user表写入数据
from django.contrib.auth.models import User
# 创建普通用户
User.objects.create_user(username=username,password=password)
# 不能用create方法: create写入的密码是明文,没有加密处理
5.3 频率
限制某个接口单位时间内可被访问的频率
使用提供的频率类
drf提供的匿名用户频率类非常好用,所以我们一般只自定义限制登录用户的频率类
from rest_framework.throttling import BaseThrottle
AnonRateThrottle #限制匿名用户的频率类
UserRateThrottle #限制登录用户的频率类(必须是Django内置的登录认证)
ScopedRateThrottle #限制用户对于每个视图的访问频率类
SimpleRateThrottle #上面三个类都继承了该类,自定义频率类也继承它,重写get_cache_key
使用方法:配置了就能用
全局使用:settings的REST_FRAMEWORK里配置
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES': (
'rest_framework.throttling.AnonRateThrottle', #限制匿名用户
'rest_framework.throttling.UserRateThrottle', #限制登录用户(必须是Django内置的登录认证)
),
'DEFAULT_THROTTLE_RATES': {
'anon': '3/m', #限制匿名用户的次数限制
'user': '10/m', #登录用户的次数限制
}
}
局部使用:视图类配置
throttle_classes = [AnonRateThrottle,UserRateThrottle]
自定义频率类
1,写一个频率类继承SimpleRateThrottle,指明scop,必须重写get_cache_key方法,该方法返回用户相关的信息,或none
2,scope用与确定限制用户的类型,get_cache_key方法这里写业务逻辑,返回什么就根据什么限制.
3,配置,不再赘述
实现原理
源码路径分析:
rest_framework-->views-->APIView-->as_view-->dispatch-->self.initial-->self.check_throttles
核心源码
def check_throttles(self, request):
throttle_durations = []
for throttle in self.get_throttles():
if not throttle.allow_request(request, self):
# 调用视图层配置频率类的allow_request方法,该方法将用户的信息存入缓存中,每次请求前判断是否超过访问频率
throttle_durations.append(throttle.wait())
if throttle_durations:
duration = max(durations, default=None)
self.throttled(request, duration)