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:
认证组件中,如果是使用了多个认证类,会按照顺序逐一执行其中的authenticate方法:
-
返回None或无返回值,表示继续执行后续的认证类
-
返回 (user, auth) 元组,则不再继续并将值赋值给request.user和request.auth
-
抛出异常 AuthenticationFailed
(...)
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"