第五篇:DRF之认证组件

第五篇:DRF之认证组件

一、认证的书写步骤

"""步骤"""
写一个类,继承BaseAuthentication,重写类中的重写authenticate方法,认证的逻辑写在其中。
如果认证通过,返回两个值,一个值最终给了Requet对象的user。一个值给了Requet对象的auth。
如果认证失败,抛出异常,APIException或者AuthenticationFailed。
ps:从源码中看出,如果有多个认证,要将返回有两个值的放到最后。

二、认证组件的源码分析

我们写的视图类继承了APIView类,类中重写了as_view方法,并调用了父类Viewas_view方法,Viewas_view方法调用了self.dispatch(request, *args, **kwargs),此时self为视图类的对象,而APIView中的dispatch重写了View中的dispatch

dispatch中调用了self.initial(request, *args, **kwargs),我们从initial方法中可以找到认证方法。

继续打开self.perform_authentication(request),我们发现调用类request.user

这个requestAPIView重写后的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中配置两张表UserUserToken,user表中存放用户名和密码,而UserToken中存放token信息和一对一指向User表的外键。

"""models.py"""
# 用户表
class User(models.Model):
    username = models.CharField(max_length=32)
    password = models.CharField(max_length=32)

# 用户token表
class UserToken(models.Model):
    token = models.CharField(max_length=64)
    user = models.OneToOneField(to='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):
        # 拿到token数据
        token = request.query_params.get('token')
        """
        如果认证通过,返回两个值 request.user和request.auth;
        如果认证失败,抛出AuthenticationFailed异常
        """
        # 判断token是否存在
        if token:
            # 根据token去表中查找
            user_token = models.UserToken.objects.filter(token=token).first()
            # 如果token检测到数据对象
            if user_token:
                return user_token.user, token
            else:
                raise AuthenticationFailed('认证失败')
        else:
            raise AuthenticationFailed('请求地址中需要携带token')

1、局部配置认证组件

视图类中局部认证配置方式如下。

from rest_framework.views import APIView
from rest_framework.response import Response
from app01.app_auth import MyAuthentication
from app01 import models
import uuid

# 用户登录,不需要认证
class Login(APIView):
    def post(self, request):
        # 根据请求拿到相关的数据
        username = request.data.get('username')
        password = request.data.get('password')
        # 根据用户名和密码得到用户对象
        user_obj = models.User.objects.filter(username=username, password=password).first()
        # 判断用户对象是否存在
        if user_obj:
            # 生成随机字符串代表token
            token_num = str(uuid.uuid4())
            print(token_num)  # 22e08528-b7d7-4798-b392-5824cd5931fc
            # 将token写入到数据库中
            """使用 models.UserToken.objects.create(token=token,user=user) 不好,用它每次登陆都会记录一条,不好,如有有记录"""
            # 默认修改的defaults={token: 'token'}, 其余字段也需传入 【update_or_create有则更新,没有则创建】
            models.UserToken.objects.update_or_create(defaults={'token': token_num}, user=user_obj)
            # 返回json格式数据
            return Response({'status': 100, 'msg': '登陆成功', 'token': token_num})
        else:
            return Response({'status': 101, 'msg': '用户名或密码错误'})

# 登录之后可以显示下相关数据
class TestView(APIView):
    # 局部认证
    authentication_classes = [MyAuthentication]

    def get(self, request):
        # 观察源码 我们发现request.user为MyAuthentication返回的第一个参数
        print(request.user.username)  # # 拿到验证通过的用户的用户名
        print(request.auth)  # 返回的第二个参数 token:e47eb245-2abd-475c-b871-38ba4612b673
        return Response({'msg': '我是认证通过之后才能显示的数据'})

2、全局配置认证组件

那么想要全局配置,局部禁用该如何操作呢?方式如下。

我们在settings.py中书写如下代码。

# 配置全局认证的类
REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": ["app01.app_auth.MyAuthentication"]
}

views.py中如下操作。

from rest_framework.views import APIView
from rest_framework.response import Response
from app01.app_auth import MyAuthentication
from app01 import models
import uuid

# 用户登录,不需要认证
class Login(APIView):
    # 局部禁用认证
    authentication_classes = []
    def post(self, request):
        # 根据请求拿到相关的数据
        username = request.data.get('username')
        password = request.data.get('password')
        # 根据用户名和密码得到用户对象
        user_obj = models.User.objects.filter(username=username, password=password).first()
        # 判断用户对象是否存在
        if user_obj:
            # 生成随机字符串代表token
            token_num = str(uuid.uuid4())
            print(token_num)  # 22e08528-b7d7-4798-b392-5824cd5931fc
            # 将token写入到数据库中
            """使用 models.UserToken.objects.create(token=token,user=user) 不好,用它每次登陆都会记录一条,不好,如有有记录"""
            # 默认修改的defaults={token: 'token'}, 其余字段也需传入
            models.UserToken.objects.update_or_create(defaults={'token': token_num}, user=user_obj)
            # 返回json格式数据
            return Response({'status': 100, 'msg': '登陆成功', 'token': token_num})
        else:
            return Response({'status': 101, 'msg': '用户名或密码错误'})

# 登录之后可以显示下相关数据
class TestView(APIView):
    def get(self, request):
        # 观察源码 我们发现request.user为MyAuthentication返回的第一个参数
        print(request.user.username)  # # 拿到验证通过的用户的用户名
        print(request.auth)  # 返回的第二个参数 token:e47eb245-2abd-475c-b871-38ba4612b673
        return Response({'msg': '我是认证通过之后才能显示的数据'})
posted @ 2021-07-21 22:45  YangYi215  阅读(189)  评论(0编辑  收藏  举报