欢迎来到赛兔子家园

drf认证组件(Authentication)

在开发API过程中,有些功能需要登录才能访问,有些无需登录。drf中的认证组件主要就是用来实现此功能。

关于认证组件,我们用案例的形式,先来学习常见应用场景,然后再来剖析源码。
案例1:

项目要开发3个接口,其中1个无需登录接口、2个必须携带token才能访问的接口。

实现步骤:

编写类(认证组件) ----> 应用认证组件(局部)

urls.py

from django.urls import path

from apps.api import views

urlpatterns = [
    path('user/', views.UserView.as_view()),
    path('order/', views.OrderView.as_view()),
    path('info/', views.InfoView.as_view()),

]

views.py

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed


# 认证组件
class MyAuthentication(BaseAuthentication):
    def authenticate(self, request):
        token = request.query_params.get("token")
        if not token:
            raise AuthenticationFailed({"code": 1002, "msg": "认证失败"})
        return "赛兔子", token

    def authenticate_header(self, request):
        return "Token"


class UserView(APIView):
    """无需登录接口 http://127.0.0.1:8000/api/auth/user/"""
    authentication_classes = []
    def get(self, request):
        return Response("用户信息")


class OrderView(APIView):
    """需要登录接口"""
    authentication_classes = [MyAuthentication, ]
    def get(self, request):
        message = f"{request.user}的订单信息"
        return Response(message)


class InfoView(APIView):
    """需要登录接口"""
    authentication_classes = [MyAuthentication, ]
    def get(self, request):
        message = f"{request.user}的用户信息"
        return Response(message)

浏览分别输入:

http://127.0.0.1:8000/user/    返回: 用户信息
http://127.0.0.1:8000/order/ 认证失败

携带token参数值(任意值都可以)访问:http://127.0.0.1:8000/order/?token=33333

http://127.0.0.1:8000/api/auth/info/?token=33333

认证组件中返回的两个值,分别赋值给:request.user 和 request.auth

案例2:

项目要开发100个接口,其中1个无需登录接口、99个必须登录才能访问的接口。

此时,就需要用到drf的全局配置(认证组件的类不能放在视图view.py中,会因为导入APIView导致循环引用)。

django根目录\utils\auth.py

from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed


# 认证组件
class MyAuthentication(BaseAuthentication):
    def authenticate(self, request):
        token = request.query_params.get("token")
        if not token:
            raise AuthenticationFailed({"code": 1002, "msg": "认证失败"})
        return "赛兔子", token

    def authenticate_header(self, request):
        return "Token"

 settings.py

# drf配置
REST_FRAMEWORK = {
    "UNAUTHENTICATED_USER": None,
    "UNAUTHENTICATED_TOKEN": None,
    "DEFAULT_AUTHENTICATION_CLASSES": [
        "utils.ext.auth.MyAuthentication", # 认证组件全局配置
    ],
}

views.py

from rest_framework.views import APIView
from rest_framework.response import Response


class UserView(APIView):
    """无需登录接口 http://127.0.0.1:8000/api/auth/user/"""
    authentication_classes = []

    def get(self, request):
        return Response("用户信息")


class OrderView(APIView):
    """需要登录接口"""

    def get(self, request):
        message = f"{request.user}的订单信息"
        return Response(message)


class InfoView(APIView):
    """需要登录接口"""

    def get(self, request):
        message = f"{request.user}的用户信息"
        return Response(message)

无需携带token接口写类属性authentication_classes = [ ]即可。

案例3:

原来的认证信息只能放在URL中传递,如果程序中支持放在很多地方,例如:URL中、body、请求头中等。

认证组件中,如果是使用了多个认证类,会按照顺序逐一执行其中的authenticate方法:

  • 返回None或无返回值,表示继续执行后续的认证类

  • 返回 (user, auth) 元组,则不再继续并将值赋值给request.user和request.auth

  • 抛出异常 AuthenticationFailed(...),认证失败,不再继续向后走

django根目录\utils\auth.py

class QueryParamsAuthentication(BaseAuthentication):
    """url中传递token"""

    def authenticate(self, request):
        token = request.query_params.get("token")
        if not token:
            return
        user_object = models.UserInfo.objects.filter(token=token).first()
        if user_object:
            return user_object, token

    def authenticate_header(self, request):
        return "API"


class HeaderAuthentication(BaseAuthentication):
    """请求头传递token"""

    def authenticate(self, request):
        token = request.META.get("HTTP_AUTHORIZATION")
        if not token:
            return
        user_object = models.UserInfo.objects.filter(token=token).first()
        if user_object:
            return user_object, token

    def authenticate_header(self, request):
        return "API"


class BodyAuthentication(BaseAuthentication):
    """请求body中"""

    def authenticate(self, request):
        token = request.data.get("token")
        if not token:
            return
        user_object = models.UserInfo.objects.filter(token=token).first()
        if user_object:
            return user_object, token


class NoAuthentication(BaseAuthentication):
    """都没有携带返回认证失败"""

    def authenticate(self, request):
        raise AuthenticationFailed({"status": False, 'msg': "认证失败"})

    def authenticate_header(self, request):
        return "API"

settings.py

# drf配置
REST_FRAMEWORK = {
    "UNAUTHENTICATED_USER": None,
    "UNAUTHENTICATED_TOKEN": None,
    "DEFAULT_AUTHENTICATION_CLASSES": [
        "utils.ext.auth.BodyAuthentication", # 认证组件全局配置
        "utils.ext.auth.QueryParamsAuthentication",
        "utils.ext.auth.HeaderAuthentication",
        "utils.ext.auth.NoAuthentication",
    ],
}

总结:

认证组件 = [认证类,认证类,认证类,] -> 执行每个认证类中的authenticate方法:

  • 认证成功或失败,不会在执行后续的认证类
  • 返回None,执行后续的认证类。

源码分析:

class APIView(object):
    authentication_classes = 读取配置文件中的列表

    def dispatch(self):
        self.authentication_classes


class UserView(APIView):
    # authentication_classes = [11,22,3,44]

    obj = UserView()
    obj.dispatch()

认证组件源码
- 加载认证组件,本质就是实例化每个认证类的对象,并封装到request对象。

多个认证类
- 都返回None,都没有认证成功 -> 视图是否会被执行? 视图函数会执行,只不过 self.user self.auth = None

class QueryParamsAuthentication(BaseAuthentication):
    """url中传递token"""

    def authenticate(self, request):
        token = request.query_params.get("token")
        if not token:
            return
        user_object = models.UserInfo.objects.filter(token=token).first()
        if user_object:
            return user_object, token

    def authenticate_header(self, request):
        return "API"


class HeaderAuthentication(BaseAuthentication):
    """请求头传递token"""

    def authenticate(self, request):
        token = request.META.get("HTTP_AUTHORIZATION")
        if not token:
            return
        user_object = models.UserInfo.objects.filter(token=token).first()
        if user_object:
            return user_object, token

    def authenticate_header(self, request):
        return "API"


class BodyAuthentication(BaseAuthentication):
    """请求body中"""

    def authenticate(self, request):
        token = request.data.get("token")
        if not token:
            return
        user_object = models.UserInfo.objects.filter(token=token).first()
        if user_object:
            return user_object, token


class NoAuthentication(BaseAuthentication):
    """都没有携带返回认证失败"""

    def authenticate(self, request):
        raise AuthenticationFailed({"status": False, 'msg': "认证失败"})

    def authenticate_header(self, request):
        return "API"

posted on 2024-03-14 21:19  赛兔子  阅读(7)  评论(0编辑  收藏  举报

导航