04 DRF-认证
在开发API过程中,有些功能需要登录才能访问,有些无需登录。drf中的认证组件主要就是用来实现此功能。
关于认证组件,我们用案例的形式,先来学习常见的用用场景,然后再来剖析源码。
1 案例1
项目要开发3个接口,其中1个无需登录接口、2个必须登录才能访问的接口。




在浏览器上中访问:
/order/token=xxxdsfsdfdf
认证组件中返回的两个值,分别赋值给:request.user和request.auth。
2 案例2
项目要开发100个接口,其中1个无需登录接口、99个必须登录才能访问的接口。
此时,就需要用到drf的全局配置(认证组件的类不能放在视图view.py中,会因为导入APIView导致循环引用)。
settings.py
REST_FRAMEWORK = {
"UNAUTHENTICATED_USER": None,
"DEFAULT_AUTHENTICATION_CLASSES": ['ext.auth.MyAuthentication']
}
如果此时你在视图类中,也配置了如下
authentication_classes = [MyAuthentication, ]
在drf中,优先去全局中读取,在去视图类中读取。视图类中没有写,默认读全局。如果全局和视图类都存在,则按视图类的中的认证为准
注意:认证类一定不要写在views视图中,不然会报错。

3 案例3
项目要开发100个接口,其中1个无需登录接口、98个必须登录才能访问的接口、1个公共接口(未登录时显示公共/已登录时显示个人信息)。

4 案例4
项目要开发100个接口,其中1个无需登录接口、98个必须登录才能访问的接口、1个公共接口(未登录时显示公共/已登录时显示个人信息)。
原来的认证信息只能放在URL中传递,如果程序中支持放在很多地方,例如:URL中、请求头中等。
认证组件中,如果是使用了多个认证类,会按照顺序逐一执行其中的authenticate方法
- 返回None或无返回值,表示继续执行后续的认证类
- 返回 (user, auth) 元组,则不再继续并将值赋值给request.user和request.auth
- 抛出异常
AuthenticationFailed(...),认证失败,不再继续向后走。

5 源码分析
点击查看代码
class Request:
def __init__(self, request, authenticators=None):
self.authenticators = authenticators or ()
@property
def user(self):
if not hasattr(self, '_user'):
with wrap_attributeerrors():
self._authenticate()
return self._user
@user.setter
def user(self, value):
self._user = value
self._request.user = value
def _authenticate(self):
# 读取每个认证组件的对象,执行 authenticate 方法,self=request对象
for authenticator in self.authenticators:
try:
user_auth_tuple = authenticator.authenticate(self)
except exceptions.APIException:
self._not_authenticated()
raise
if user_auth_tuple is not None:
self._authenticator = authenticator
self.user, self.auth = user_auth_tuple
return
self._not_authenticated()
def _not_authenticated(self): # 匿名访问
self._authenticator = None
if api_settings.UNAUTHENTICATED_USER:
self.user = api_settings.UNAUTHENTICATED_USER()
else:
self.user = None
if api_settings.UNAUTHENTICATED_TOKEN:
self.auth = api_settings.UNAUTHENTICATED_TOKEN()
else:
self.auth = None
class APIView(View):
authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
def perform_authentication(self, request):
request.user
def initial(self, request, *args, **kwargs):
self.perform_authentication(request)
self.check_permissions(request)
self.check_throttles(request)
def get_authenticators(self):
return [auth() for auth in self.authentication_classes]
def initialize_request(self, request, *args, **kwargs):
parser_context = self.get_parser_context(request)
return Request(
request,
authenticators=self.get_authenticators(), # [对象1, 对象2,...]
...
)
def dispatch(self, request, *args, **kwargs):
self.args = args # url传递的参数接收
self.kwargs = kwargs # url传递的参数接收
# 第一步:请求的封装(django的request对象 + authenticators认证组件)—> 加载认证组件的过车用
request = self.initialize_request(request, *args, **kwargs)
self.request = request
self.headers = self.default_response_headers # deprecate?
try:
# 第二步:request
self.initial(request, *args, **kwargs)
# Get the appropriate handler method
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(),
self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
# 第三步:执行视图函数
response = handler(request, *args, **kwargs)
except Exception as exc:
response = self.handle_exception(exc)
self.response = self.finalize_response(request, response, *args, **kwargs)
return self.response
class UserView(APIView):
authentication_classes = []
def get(self, request):
print(request.user, request.auth)
return Response('UserView.')
# 请求到来时:
obj = UserView()
obj.dispatch()
ps:
多个认证类:
- 都返回None,都没有认证成功 -> 视图会被执行,只不过 self.user=None self.auth=None
- 案例:
class MyAuthentication(BaseAuthentication):
def authenticate(self, request):
token = request.query_params.get('token') # 获取url中的参数
if token:
return 'lxx', token
return
class HeaderAuthentication(BaseAuthentication):
def authenticate(self, request):
token = request.meta.get('token') # 获取url中的参数
if token:
return 'lxx', token
return
class BodyrAuthentication(BaseAuthentication):
def authenticate(self, request):
token = request.data.get('token') # 获取url中的参数
if token:
return 'lxx', token
return
class NoAuthentication(BaseAuthentication):
def authenticate(self, request):
raise AuthenticationFailed({'code': 101, 'error': '认证失败'})
class UserView(APIView):
authentication_classes = [MyAuthentication, HeaderAuthentication, BodyrAuthentication, NoAuthentication]
def get(self, request):
print(request.user, request.auth)
return Response('UserView.')
6 authenticate_header
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
class MyAuthentication(BaseAuthentication):
def authenticate(self, request):
# 去做用户认证:
# 1.读取请求传递的token
# 2.校验合法性
# 3.返回值
# 3.1 返回元组(11, 22) 认证成功 request.user request.auth
# 3.2 抛出异常 认证失败 -> 返回错误信息
# 3.2 返回None,找下一个认证类 多个认证类 [类1, 类2, ...] 直到不返回None时结束 --> 都返回None (匿名用户)
# /xx/xx/xx/?token=123123123
# token = request._request.GET.get('token')
token = request.query_params.get('token') # 获取url中的参数
if token:
return 'lxx', token
# raise AuthenticationFailed('认证失败.')
raise AuthenticationFailed({'code': 101, 'error': '认证失败'})
def authenticate_header(self, request):
# 没写该方法:如果访问失败,返回的状态码是403
# 写了该方法:如果访问失败,返回的状态码是401
return 'token' # 随便返回一个值就行
# return 'Basic realm="API"' # 会跳出弹出框

7 扩展-抽象类
父类约束子类中必须要有相应方法,那么必须定义以下:
class Foo(object):
# 对子类进行约束,子类中必须要有定义的这个方法(类似于接口,抽象类)
def f1(self):
raise NotImplementedError("子类必须要有该方法,不然报错喽")
class News(Foo):
def f1(self):
...
8 小小应用


浙公网安备 33010602011771号