第五篇:DRF之认证组件
一、认证的书写步骤
"""步骤"""
1 写一个类,继承BaseAuthentication,重写类中的重写authenticate方法,认证的逻辑写在其中。
如果认证通过,返回两个值,一个值最终给了Requet对象的user。一个值给了Requet对象的auth。
如果认证失败,抛出异常,APIException或者AuthenticationFailed。
二、认证组件的源码分析
我们写的视图类继承了APIView
类,类中重写了as_view
方法,并调用了父类View
的as_view
方法,as_view
方法中调用了self.dispatch(request, *args, **kwargs)
,此时self为视图类的对象,而APIView
中的dispatch
重写了View
中的dispatch
。
dispatch
中调用了self.initial(request, *args, **kwargs)
,我们从initial
方法中可以找到认证方法。
继续打开self.perform_authentication(request)
,我们发现调用类request.user
这个request
为重写后的request
方法,我们在Request
类中查找user
方法。
我们继续查看self._authenticate()
,可以得出。
至于为什么self.authenticators
中放的时认证类对象的列表?
而self.get_authenticators()
中表明了原因。
总结:
1 Requet对象的user----》dispatch方法---》self.initial(request, *args, **kwargs)---->有认证,权限,频率
2 只读认证源码: self.perform_authentication(request)
3 self.perform_authentication(request)就一句话:request.user,需要去drf的Request对象中找user属性(方法)
4 Request类中的user方法,刚开始来,没有_user,走 self._authenticate()
5 核心,就是Request类的 _authenticate(self):
def _authenticate(self):
# 遍历拿到一个个认证器,进行认证
# self.authenticators配置的一堆认证类产生的认证类对象组成的 list
# 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()
三、认证组件的使用
我们实现配置这样的认证类,代码如下所示。在models.py
中配置两张表User
和UserToken
,user
表中存放用户名和密码,而UserToken
中存放token
信息和一对一指向User
表的外键。
"""models.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):
token=models.CharField(max_length=64)
user=models.OneToOneField(to=User,on_delete=models.CASCADE) #一对一关联到User表
"""app_auth.py"""
# 类继承BaseAuthentication
from rest_framework.authentication import BaseAuthentication
# 抛出异常使用
from rest_framework.exceptions import AuthenticationFailed
# 导入表
from app01 import models
class MyAuthentication(BaseAuthentication):
def authenticate(self, request):
# 认证逻辑,如果认证通过,返回两个值
# 如果认证失败,抛出AuthenticationFailed异常
token=request.GET.get(‘token‘)
if token:
user_token = models.UserToken.objects.filter(token=token).first()
# 认证通过
if user_token:
return user_token.user,token
else:
raise AuthenticationFailed(‘认证失败‘)
else:
raise AuthenticationFailed(‘请求地址中需要携带token‘)
1、局部配置认证组件
视图类中局部认证配置方式如下。
from rest_framework.views import APIView
from app01.app_auth import MyAuthentication
import uuid
# 验证是否登录
class TestView(APIView):
# 局部配置认证
authentication_classes = [MyAuthentication]
def get(self,request):
# 观察源码 我们发现request.user为MyAuthentication返回的第一个参数
print(request.user.username) # 拿到验证通过的用户的用户名
return Response({‘msg‘:‘我是测试‘})
class LoginView(APIView):
def post(self,request):
username=request.data.get(‘username‘)
password=request.data.get(‘password‘)
user=models.User.objects.filter(username=username,password=password).first()
if user:
# 登陆成功,生成一个随机字符串
token=uuid.uuid4()
# 存到UserToken表中
# models.UserToken.objects.create(token=token,user=user)# 用它每次登陆都会记录一条,不好,如有有记录
# update_or_create有就更新,没有就新增
models.UserToken.objects.update_or_create(defaults={‘token‘:token},user=user)
return Response({‘status‘:100,‘msg‘:‘登陆成功‘,‘token‘:token})
else:
return Response({‘status‘: 101, ‘msg‘: ‘用户名或密码错误‘})
2、全局配置认证组件
那么想要全局配置,局部禁用该如何操作呢?方式如下。
我们在settings.py
中书写如下代码。
# 配置全局认证的类
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": ["app01.app_auth.MyAuthentication"]
}
而views.py
中如下操作。
class TestView(APIView):
def get(self,request):
print(request.user.username)
return Response({‘msg‘: ‘我是测试‘})
class LoginView(APIView):
# 局部禁用
authentication_classes = []
def post(self,request):
username = request.data.get(‘username‘)
password = request.data.get(‘password‘)
user = models.User.objects.filter(username=username, password=password).first()
if user:
# 登陆成功,生成一个随机字符串
token = uuid.uuid4()
# 存到UserToken表中
# models.UserToken.objects.create(token=token,user=user)# 用它每次登陆都会记录一条,不好,如有有记录
# update_or_create有就更新,没有就新增
models.UserToken.objects.update_or_create(defaults={‘token‘: token}, user=user)
return Response({‘status‘: 100, ‘msg‘: ‘登陆成功‘, ‘token‘: token})
else:
return Response({‘status‘: 101, ‘msg‘: ‘用户名或密码错误‘})