对于DRF框架,学会使用很简单,如果能从源码的角度理解它的一些常用功能的实现,那么对于我们去学另外一门新的框架的时候也可以得心应手。
视图层
class OrderView(APIView):
authentication_classes = [Myauthenticate,]
def get(self, request, *args, **kwargs):
return JsonResponse('ok')
新建utils文件夹创建专门做认证的py文件
必须实现authenticate和authenticate_header方法(这个是认证失败的时候,给浏览器返回的响应的响应头)
from rest_framework import exceptions
from rest_framework.authentication import BaseAuthentication
from api import models
class Myauthenticate(BaseAuthentication):
def authenticate(self, request):
token = request._request.GET.get('token')
token_obj = models.UserToken.objects.filter(token=token).first()
"""
在Django的源码里边,这些返回都是很严格的
如果认证失败,必须抛异常,不能使用return返回东西
如果认证成功,必须返回一个元组
"""
if not token_obj:
raise exceptions.AuthenticationFailed('认证失败')
# 认证成功必须返回一个元组,
# 元组的第一个元素DRF会放到request.user里边,
# 第二个元素给request.auth
return (token_obj.user, token_obj)
def authenticate_header(self, request):
pass
记住请求到view里边,如果是CBV的话,会先到类的dispatch里边,然后dispatch根据请求类型找到GET(),POST()等方法然后执行。
先来看看Django的APIview的dispatch方法源码,里边我注释了,大家可以和我一起研究下,其实不难
def dispatch(self, request, *args, **kwargs):
"""
`.dispatch()` is pretty much the same as Django's regular dispatch,
but with extra hooks for startup, finalize, and exception handling.
"""
self.args = args
self.kwargs = kwargs
# 第一步:对原生的request进行了封装,
# 待会我们看看initialize_request做了啥
request = self.initialize_request(request, *args, **kwargs)
self.request = request
self.headers = self.default_response_headers # deprecate?
# 第二步
#处理版权信息
#认证
#权限
#请求用户进行访问频率的限制
try:
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
initialize_request的源码
下图可看到initialize_request
加了四个东西,request不再是原来的request,添加了新的功能。其中一个就是用户认证
def initialize_request(self, request, *args, **kwargs):
"""
Returns the initial request object.
"""
parser_context = self.get_parser_context(request)
return Request(
request,
parsers=self.get_parsers(),
# 下面这句一看就应该是做用户认证的,点进去看做了啥
authenticators=self.get_authenticators(),
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)
get_authenticators(身份验证器源码)
def get_authenticators(self):
"""
Instantiates and returns the list of authenticators that this view can use.
"""
"""
由源码可见这是一个列表推导式,此方法返回了一个auth对象的列表
"""
return [auth() for auth in self.authentication_classes]
接下来我们看看authentication_classes
里面是啥
可见这是默认的,如果我们自己定义了,那么就会用我们的,因为它是从我们写的类里面找,找不到再去父类APIview中找
然后我们点api_settings
进去看看是啥玩意
api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS)
接下来点击它继承的defaults类看看是什么
这里的源码写了一大堆,写了很多默认的类,我们主要看它那两个默认认证的类
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication'
],
走到这里的时候,pycharm没法点往下走了,我们导入这两个类看看
from rest_framework.authentication import SessionAuthentication
from rest_framework.authentication import BaseAuthentication
查看BaseAuthentication
源码
class BaseAuthentication(object):
"""
All authentication classes should extend BaseAuthentication.
"""
def authenticate(self, request):
"""
Authenticate the request and return a two-tuple of (user, token).
"""
raise NotImplementedError(".authenticate() must be overridden.")
def authenticate_header(self, request):
"""
Return a string to be used as the value of the `WWW-Authenticate`
header in a `401 Unauthenticated` response, or `None` if the
authentication scheme should return `403 Permission Denied` responses.
"""
pass
从源码中也可以看到authenticate方法必须被重写,否则会抛异常
class BaseAuthentication
下面还有个BasicAuthentication
,这是做具体认证的流程,从headers里面能获取用户名和密码
class BasicAuthentication(BaseAuthentication):
"""
HTTP Basic authentication against username/password.
"""
www_authenticate_realm = 'api'
def authenticate(self, request):
"""
Returns a `User` if a correct username and password have been supplied
using HTTP Basic authentication. Otherwise returns `None`.
"""
auth = get_authorization_header(request).split()
if not auth or auth[0].lower() != b'basic':
return None
if len(auth) == 1:
msg = _('Invalid basic header. No credentials provided.')
raise exceptions.AuthenticationFailed(msg)
elif len(auth) > 2:
msg = _('Invalid basic header. Credentials string should not contain spaces.')
raise exceptions.AuthenticationFailed(msg)
try:
auth_parts = base64.b64decode(auth[1]).decode(HTTP_HEADER_ENCODING).partition(':')
except (TypeError, UnicodeDecodeError, binascii.Error):
msg = _('Invalid basic header. Credentials not correctly base64 encoded.')
raise exceptions.AuthenticationFailed(msg)
userid, password = auth_parts[0], auth_parts[2]
return self.authenticate_credentials(userid, password, request)
def authenticate_credentials(self, userid, password, request=None):
"""
Authenticate the userid and password against username and password
with optional request for context.
"""
credentials = {
get_user_model().USERNAME_FIELD: userid,
'password': password
}
user = authenticate(request=request, **credentials)
if user is None:
raise exceptions.AuthenticationFailed(_('Invalid username/password.'))
if not user.is_active:
raise exceptions.AuthenticationFailed(_('User inactive or deleted.'))
return (user, None)
def authenticate_header(self, request):
return 'Basic realm="%s"' % self.www_authenticate_realm
当然restfulframework默认定义了两个类。我们也可以自定制类,自己有就用自己的了,自己没有就去找父类的了,但是里面必须实现authenticate方法,不然会报错。
首先看self.initial(request, *args, **kwargs)
源码
def initial(self, request, *args, **kwargs):
"""
Runs anything that needs to occur prior to calling the method handler.
"""
self.format_kwarg = self.get_format_suffix(**kwargs)
# Perform content negotiation and store the accepted info on the request
neg = self.perform_content_negotiation(request)
request.accepted_renderer, request.accepted_media_type = neg
# Determine the API version, if versioning is in use.
version, scheme = self.determine_version(request, *args, **kwargs)
request.version, request.versioning_scheme = version, scheme
# Ensure that the incoming request is permitted
self.perform_authentication(request)
self.check_permissions(request)
self.check_throttles(request)
可以看到做认证是通过self.perform_authentication(request)
,点进去看看
def perform_authentication(self, request):
"""
Perform authentication on the incoming request.
Note that if you override this and simply 'pass', then authentication
will instead be performed lazily, the first time either
`request.user` or `request.auth` is accessed.
"""
request.user
它返回了一个request.user,注意这个request已经不是原生的request,他是封装过的,
导入Request对象,找到它的user方法
@property
def user(self):
"""
Returns the user associated with the current request, as authenticated
by the authentication classes provided to the request.
"""
if not hasattr(self, '_user'):
with wrap_attributeerrors():
self._authenticate()
return self._user
可以看到user()被设置为静态属性,所以上面没有加括号就直接调用了,点击去看看_authenticate
def _authenticate(self):
"""
Attempt to authenticate the request using each authentication instance
in turn.
"""
# 循环认证类的所有对象
for authenticator in self.authenticators:
try:
"""
执行认证类的authenticate方法,
1.如果authenticate方法抛出异常,执行_not_authenticated()
2.有返回值,必须是元组:(request.user,request.auth)
把元组的第一个元素赋值给self.user(用户),
第二个元素赋值给self.auth(用户的token)
3.返回None,就不管,下一个认证来处理(raise是向上抛出异常)
"""
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()
然后我们在来看看_not_authenticated()是干啥的,,也就是说如果认证后返回None,会怎么样
def _not_authenticated(self):
"""
Set authenticator, user & authtoken representing an unauthenticated request.
Defaults are None, AnonymousUser & None.
"""
self._authenticator = None
# Authentication
# 'UNAUTHENTICATED_USER': 'django.contrib.auth.models.AnonymousUser',
# 'UNAUTHENTICATED_TOKEN': 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
可见,如果上面都没处理,我就加个默认值AnonymousUser和None,把request.user赋值给AnonymousUser,request.auth赋值给None。
perform_authentication
user
,user已经被声明为静态属性,_authenticate()
authenticators
的对象,执行对象的authenticate
方法。authenticate
方法有三种情况authenticate
方法返回None,代表什么也不做,由下一个类来进行处理,如果有返回值,返回值一定是个元组,元组的两个元素分别赋值给request.user
和request.auth
,第三种情况是抛异常_not_authenticated
方法_not_authenticated
方法说明跳过了所有的认证,默认用户和token使用配置文件进行设置