restframework 认证源码流程
作者:互联网
一.请求来到之后,都要先执行dispatch方法,根据请求方式不同触发get/post/put/delete等方法
注意: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进行加工(添加数据) 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 # 第三步、执行:get/post/put/delete函数 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
二.接下来 我们从第一步慢慢入手 ,分析具体是对request添加了哪些数据
1.首先request = self.initialize_request(request, *args, **kwargs)点进去,你会发现: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(), #解析数据,默认的有三种方式,可点进去看 #self.get_authenticator优先找自己的,没有就找父类的 authenticators=self.get_authenticators(), #获取认证相关的所有类并实例化,传入request对象供Request使用 negotiator=self.get_content_negotiator(), parser_context=parser_context )
2.获取认证相关的具体的类列表 authenticators=self.get_authenticators()
def get_authenticators(self): """ Instantiates and returns the list of authenticators that this view can use. """ #返回的是对象列表 return [auth() for auth in self.authentication_classes] #[SessionAuthentication,BaseAuthentication]
3.查看认证的类:self.authentication_classes
authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES #默认的,如果自己有会优先执行自己的
4.接着我们走进api_settings中看看这些系统的认证器
DEFAULTS = { # Base API policies 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework.authentication.SessionAuthentication', #这时候就找到了他默认认证的类了,可以导入看看 'rest_framework.authentication.BasicAuthentication' ),
5.我们导入类看看里面具体做了什么事情 上述两个SessionAuthentication和BasicAuthentication都是继承了BaseAuthentication
from rest_framework.authentication import SessionAuthentication from rest_framework.authentication import BaseAuthentication
6.里面有两个方法分别是 authenticate()方法 和 authenticate_header()方法
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
具体认证处理,那我们就看一看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 #返回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(':') #用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
看上述源码 主要实现的是authenticate()方法 其返回值是一个 元组 return (user,None) user对象 和 None 。因此我们之后也可以模仿他写一个自定义认证类
7.第一步操作主要是将这些认证器变成列表添加进request对象中,具体的认证流程 在 第二步 下面我们回到最初的dispatch 看第二步
self.initial(request,*args,**kwargs) 这一步主要是 1.处理版本信息 2. 认证 3. 权限 4.请求用户进行访问频率的限制
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. #2.1 处理版本信息 version, scheme = self.determine_version(request, *args, **kwargs) request.version, request.versioning_scheme = version, scheme # Ensure that the incoming request is permitted #2.2 认证 self.perform_authentication(request) # 2.3 权限 self.check_permissions(request) # 2.4 请求用户进行访问频率的限制 self.check_throttles(request)
8.进来后我们看到了 一行关键的 认证代码 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了
9.出乎意料 就他妈一行代码 reqeust.user 你会发现想继续看看user是什么时点也点不进去,很头疼。不要急,我们接下来稍微分析一下
request是对象 它既然能点出来 而且在没有做任何返回的情况下 我们可以断定 user是一个属性方法 因此我们可以去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'): #判断有没有user 没有的话就执行下面的 self._authenticate() with wrap_attributeerrors(): self._authenticate() # return self._user #返回user
10.点进来后 又惊喜的发现 有self._authenticate()方法 开始用户认证
def _authenticate(self): """ Attempt to authenticate the request using each authentication instance in turn. """ #循环对象列表 for authenticator in self.authenticators: try: #执行每一个对象的authenticate 方法 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 #返回一个元组,user,和auth,赋给了self, # 只要实例化Request,就会有一个request对象,就可以request.user,request.auth了 return self._not_authenticated()
11.这里user_auth_tuple = authenticator.authenticate(self) 开始将之前reqeust对象中的认证器列表循环遍历 并进行验证
验证成功就会 返回一个元组 并解压赋值给 用户 user 和 用户Token auth
如果认证不成功:执行 self._not_authenticated()方法
def _not_authenticated(self): """ Set authenticator, user & authtoken representing an unauthenticated request. Defaults are None, AnonymousUser & None. """ #如果跳过了所有认证,默认用户和Token和使用配置文件进行设置 self._authenticator = None # if api_settings.UNAUTHENTICATED_USER: self.user = api_settings.UNAUTHENTICATED_USER() # 默认值为:匿名用户AnonymousUser else: self.user = None # None 表示跳过该认证 if api_settings.UNAUTHENTICATED_TOKEN: self.auth = api_settings.UNAUTHENTICATED_TOKEN() # 默认值为:None else: self.auth = None # (user, token) # 表示验证通过并设置用户名和Token; # AuthenticationFailed异常
12.到此就是验证流程整个过程
标签:authenticate,self,request,auth,认证,authentication,源码,user,restframework 来源: https://www.cnblogs.com/s686zhou/p/11720806.html