django drf 认证
我们先了解一下django的调度过程,使用工具vscode,右键方法或者类,可以看到django源代码内容
Django如何处理请求
当用户从Django支持的站点请求页面时,系统将遵循以下算法来确定要执行的python代码:
1.Django确定要使用的根urlconf模块。通常,这是 ROOT_URLCONF 设置,但如果传入 HttpRequest 对象具有 urlconf 属性(由中间件设置),其值将用于替代 ROOT_URLCONF 设置。
2.Django加载该python模块并查找变量 urlpatterns . 这应该是一个 sequence 属于 django.urls.path() 和/或 django.urls.re_path() 实例。
3.Django按顺序运行每个URL,并在第一个匹配请求的URL的模式处停止,匹配 path_info 一旦其中一个URL模式匹配,Django导入并调用给定的视图.
4.如果没有匹配的URL模式,或者在此过程中的任何一点期间引发异常,Django将调用适当的错误处理视图。
#路由层
urlpatterns = [
re_path('list/(.*?)/', views.SnippetList.as_view()),
path('<int:pk>/', views.SnippetDetail.as_view()),
path('users/', views.UserList.as_view()),
path('users/<int:pk>/', views.UserDetail.as_view()),
path('api_root', views.api_root),
1.路由器接受请求时会执行视图类(Views)的as_view方法,而as_view方法中会执行dispacth方法,然后在根据请求的类型执行相应的方法(get、post等)。django rest framework中的视图类需要继承APIView,请求到达视图类会执行视图类的as_view方法
@classmethod
def as_view(cls, **initkwargs):
"""
将原始类存储在视图函数中。
这允许我们在执行URL时发现关于视图的信息
反向查找。用于生成面包屑。
"""
if isinstance(getattr(cls, 'queryset', None), models.query.QuerySet): # 对获取cls里面的queryset的值,进行子父类比较
def force_evaluation():
raise RuntimeError(
'Do not evaluate the `.queryset` attribute directly, '
'as the result will be cached and reused between requests. '
'Use `.all()` or call `.get_queryset()` instead.'
)
cls.queryset._fetch_all = force_evaluation
view = super(APIView, cls).as_view(**initkwargs) #执行父类as_view()方法
view.cls = cls
view.initkwargs = initkwargs
#:注意:基于会话的身份验证是经CSRF显式验证的,
# 所有其他身份验证都是CSRF豁免的。
return csrf_exempt(view)
2.因为APIView中as_view又执行了父类的as_view方法,而APIView的父类是View类以下是View类中的as_view()的源码:
主要是 ,验证请求方法,返回视图的名称引用,调用self.dispatch方法
class View:
'''省略一万字'''
def as_view(cls, **initkwargs):
"""
请求-响应流程的主入口点。
"""
for key in initkwargs:
if key in cls.http_method_names: #验证请求方法
raise TypeError("You tried to pass in the %s method name as a "
"keyword argument to %s(). Don't do that."
% (key, cls.__name__))
if not hasattr(cls, key):
raise TypeError("%s() received an invalid keyword %r. as_view "
"only accepts arguments that are already "
"attributes of the class." % (cls.__name__, key))
def view(request, *args, **kwargs):
self = cls(**initkwargs) #实例化类试图,自定义的
if hasattr(self, 'get') and not hasattr(self, 'head'):
self.head = self.get
self.request = request
self.args = args
self.kwargs = kwargs
#分配视图
return self.dispatch(request, *args, **kwargs)
view.view_class = cls
view.view_initkwargs = initkwargs
#从类中获取名称和文档字符串
update_wrapper(view, cls, updated=())
#以及decorator设置的可能属性
# 比如csrf_exempt from分派
update_wrapper(view, cls.dispatch, assigned=())
#返回view函数名称引用
return view
其中的验证请求方法
http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
3.我们接着看view返回的dispatch方法时候会执行self.initialize_request方法,会对django原始的request进行封装。
def dispatch(self, request, *args, **kwargs):
"""
`.dispatch() '与Django的常规分派基本相同,但是有额外的钩子用于启动、finalize和异常处理。.
"""
self.args = args
self.kwargs = kwargs
request = self.initialize_request(request, *args, **kwargs) #对django原始的request进行封装,返回Request对象
self.request = request
self.headers = self.default_response_headers # 反对?
try:
self.initial(request, *args, **kwargs) #这里request参数实则是Request对象
#获取适当的处理程序方法,改程小写判断是否是合法的http方法
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(), #在self.http_method_not_allowed中找到对应的方法
self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
#<Response status_code=403, "text/html; charset=utf-8">
response = handler(request, *args, **kwargs) #返回response对象 和状态吗,文件,charset
print(response)
except Exception as exc:
response = self.handle_exception(exc) #通过返回适当的响应,处理发生的任何异常,或再次抛出错误。
self.response = self.finalize_response(request, response, *args, **kwargs)
return self.response
4.再看看initialize_request源码封装的内容,以下是self.initialize_request()源码:
def initialize_request(self, request, *args, **kwargs):
"""
返回初始请求对象。
"""
parser_context = self.get_parser_context(request)
return Request( #实例化Request类,
request, #django原始的request对象,封装到Request中变成self._request
parsers=self.get_parsers(),
authenticators=self.get_authenticators(), #从这里开始就是认证流程
negotiator=self.get_content_negotiator(), #开始权限验证
parser_context=parser_context
)
5.self.initialize_request()源码分析,实例化Request()类,封装原始的request,authenticators(认证),执行self.get_authenticators(),到了这里就开始django rest framework的认证流程,以下是
#self.get_authenticators()源码:
def get_authenticators(self):
"""
实例化并返回此视图可以使用的身份验证器列表
"""
return [auth() for auth in self.authentication_classes] #列表生成式返回认证类的对象列表
6.self.get_authenticators()源码分析,循环遍历self.authentication_classes,并实例化其中的每一个类返回列表。如果我们没有定义会自动使用settings中DEFAULT_AUTHENTICATION_CLASSES作为默认(全局)
class APIView(View):
# 以下策略可以全局设置,也可以按视图设置。
renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
parser_classes = api_settings.DEFAULT_PARSER_CLASSES
authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES #默认认证配置
throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES
permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES #权限认证
content_negotiation_class = api_settings.DEFAULT_CONTENT_NEGOTIATION_CLASS
metadata_class = api_settings.DEFAULT_METADATA_CLASS
versioning_class = api_settings.DEFAULT_VERSIONING_CLASS
7.继续分析APIView的dispatch方法,此时执行self.inital方法,并将封装过后的request对象(Reuqest)作为参数进行传递,下面是self.inital()方法源码:
def initial(self, request, *args, **kwargs):
"""
运行在调用方法处理程序之前需要发生的任何事情。.
"""
self.format_kwarg = self.get_format_suffix(**kwargs)
# 执行内容协商并在请求中存储接受的信息
neg = self.perform_content_negotiation(request)
request.accepted_renderer, request.accepted_media_type = neg
# 如果正在使用版本控制,请确定API版本。
version, scheme = self.determine_version(request, *args, **kwargs)
request.version, request.versioning_scheme = version, scheme
#确保传入的请求是允许的
self.perform_authentication(request) #实现认证
self.check_permissions(request)
self.check_throttles(request)
8.我们接着看 self.perform_authentication(request) 对传入的请求执行身份验证,并执行user方法
def perform_authentication(self, request):
"""
对传入的请求执行身份验证。.
请注意,如果您覆盖了这个选项并简单地‘pass’,则是身份验证
第一次也会被惰性地执行吗
的请求。用户”或“请求。身份验证的访问。.
"""
request.user
9.在self.inital方法中会执行self.perform_authentication方法,而self.perform_authentication方法用会执行request.user,是实例化后的对象
class Request(object):
"""
包装器允许增强一个标准的“HttpRequest”实例。
Kwargs:
-请求(HttpRequest)。原始请求实例。
parsers_classes(列表/元组)。的解析器
请求的内容。
authentication_classes(列表/元组)。用于尝试的身份验证
验证请求的用户。
"""
def __init__(self, request, parsers=None, authenticators=None,
negotiator=None, parser_context=None):
#断言,在函数体为false时执行
assert isinstance(request, HttpRequest), (
'The `request` argument must be an instance of '
'`django.http.HttpRequest`, not `{}.{}`.'
.format(request.__class__.__module__, request.__class__.__name__)
)
self._request = request #django原生的request封装为_request
self.parsers = parsers or () #返回为真的 不然就是实例化
self.authenticators = authenticators or ()
self.negotiator = negotiator or self._default_negotiator()
self.parser_context = parser_context
self._data = Empty
self._files = Empty
self._full_data = Empty
self._content_type = Empty
self._stream = Empty
我们接着看user方法
@property
def user(self):
"""
返回与当前请求关联的经过身份验证的用户
通过提供给请求的身份验证类。
"""
if not hasattr(self, '_user'):
with wrap_attributeerrors():
self._authenticate() #验证请求
return self._user
10.从上源码分析,在Request对象中,user属性是一个属性方法,并会执行self._authentication方法,在继续看Request对象的self._authentication方法:
def _authenticate(self):
"""
尝试使用每个身份验证实例验证请求
反过来。
"""
for authenticator in self.authenticators:
try:
#返回值为元组,(self.force_user, self.force_token)
user_auth_tuple = authenticator.authenticate(self)
#如果authenticate方法抛出异常,self._not_authenticated()执行
except exceptions.APIException:
self._not_authenticated()
raise
#返回None,表示当前认证不处理,等下一个认证来处理
if user_auth_tuple is not None:
self._authenticator = authenticator
self.user, self.auth = user_auth_tuple #返回值self.user, self.auth
return
self._not_authenticated()
self.authenticators = authenticators or ()属性,为真是实例化
self.authenticators = authenticators or ()
authenticator.authenticate(self)的方法 ,一般这是我们自己写的
def authenticate(self, request):
return (self.force_user, self.force_token)
self.not_authenticated方法
def _not_authenticated(self):
"""
设置身份验证器、用户和authtoken,表示未经身份验证的请求。
默认值为None, AnonymousUser & None。
"""
self._authenticator = None
if api_settings.UNAUTHENTICATED_USER:
self.user = api_settings.UNAUTHENTICATED_USER() #匿名用户配置,默认返回AnonymousUser
else:
self.user = None
if api_settings.UNAUTHENTICATED_TOKEN:
self.auth = api_settings.UNAUTHENTICATED_TOKEN() #None
else:
self.auth = None
11.流程:
异常验证逻辑:
1.如果有异常则执行self._not_authenticated()方法,继续向上抛异常。
2.如果有返回值必须是一个元组,分别赋值给self.user, self.auth(request.user和request.auth),并跳出循环。如果返回None,则由下一个循环处理,如果都为None,则执self._not_authenticated(),返回 (AnonymousUser,None)
3.当都没有返回值,就执行self._not_authenticated(),相当于匿名用户,没有通过认证,并且此时django会返回默认的匿名用户设置AnonymousUser,如需要单独设置匿名用户返回值,则编写需要写UNAUTHENTICATED_USER的返回值:
全局配置方法:
settings.py
#设置全局认证
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES":['API.utils.auth.Authentication',] #里面写你的认证的类的路径
}
自定义认证
rest_framework里面内置了一些认证,我们自己写的认证类都要继承内置认证类 “BaseAuthentication”
class BaseAuthentication(object):
"""
所有身份验证类都应该扩展BaseAuthentication。.
"""
def authenticate(self, request):
"""
对请求进行身份验证并返回一个二元组 (user, token).
"""
#内置的认证类,authenticate方法,如果不自己写,默认则抛出异常
raise NotImplementedError(".authenticate() must be overridden.")
def authenticate_header(self, request):
"""
返回一个字符串作为“WWW-Authenticate”的值
在“401未经验证”的回复中,或在“没有”的情况下
认证方案应该返回“403 Permission Denied”的响应。.
"""
#authenticate_header方法,作用是当认证失败的时候,返回的响应头
pass
自定义:
1)创建认证类
继承BaseAuthentication —>>1.重写authenticate方法;2.authenticate_header方法直接写pass就可以(这个方法必须写)
2)authenticate()返回值(三种)
None ----->>>当前认证不管,等下一个认证来执行
raise exceptions.AuthenticationFailed(‘用户认证失败’)
有返回值元祖形式:(元素1,元素2)
#元素1复制给request.user; 元素2复制给request.auth。
注意:self.function是实例化方法,cls.function是类方法
参考 大神教程