drf中认证源码流程
drf中认证流程
首先通过导入from rest_framework.views import APIView
,然后通过ctrl+鼠标右键进入到APIView类中,apiview中定义了许多方法,我们首先找到dispatch方法,因为定义路由时候,通过指定url找到对应的视图类,首先会执行dispatch方法
## dispatch源码 def dispatch(self, request, *args, **kwargs): self.args = args self.kwargs = kwargs # initialize_request() 返回初始请求对象 request = self.initialize_request(request, *args, **kwargs) self.request = request self.headers = self.default_response_headers # deprecate? try: # initial()处理请求方法之前要做的一些检查,其中就包括认证检查,权限检查,节流等 self.initial(request, *args, **kwargs) # Get the appropriate handler method ... # 此处代码省略,这部分代码主要就是用来获取适当的请求方法
然后我们点击self.initial(request, *args, **kwargs)
# 主要就是处理请求方法前的一些校验 def initial(self, request, *args, **kwargs): .... # 重点关注这三个 # 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 # 这边调用的是drf(APIView)的Request的user属性
然后我们点击user
(在rest_framework下的request.py文件)
#返回与当前请求关联的用户,作为已验证的用户,通过提供给请求的身份验证类。 @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
然后我们点击self._authenticate()
#开始用户认证,如果验证成功后返回元组: (用户,用户Token) def _authenticate(self): """ Attempt to authenticate the request using each authentication instance in turn. """ # [BasicAuthentication()对象,] # 循环得到每个对象 for authenticator in self.authenticators: # 这里的self指的是drf的request对象,看下面的分析 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 return self._not_authenticated()
再看下self._not_authenticated()
#如果认证成功,执行_authenticate方法,若认证失败抛出异常调用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() else: self.user = None if api_settings.UNAUTHENTICATED_TOKEN: self.auth = api_settings.UNAUTHENTICATED_TOKEN() else: self.auth = None
我们可以从APIView类中找到initialize_request()
函数,主要作用就是封装并返回初始化request对象
def initialize_request(self, request, *args, **kwargs): """ Returns the initial request object. """ parser_context = self.get_parser_context(request) return Request( request, # 封装了原生request parsers=self.get_parsers(), # [BasicAuthentication(),] #实例化对象 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. """ return [auth() for auth in self.authentication_classes] #实例化对象
我们点击self.authentication_classes
# api_settings文件中就是drf默认的验证器 class APIView(View): ... authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES ...
我们点击api_settings.DEFAULT_AUTHENTICATION_CLASSES
'DEFAULT_AUTHENTICATION_CLASSES': [ 'rest_framework.authentication.SessionAuthentication', # session认证 'rest_framework.authentication.BasicAuthentication' # 基本认证 ],
然后我们来看一下上面的两个认证器类(rest_framework/authentication.py)
# 导入 from rest_framework.authentication import BaseAuthentication # 点开BaseAuthentication # 可以看到这是一个父类,所有认证器类都需要继承它,并且需要重写里面的两个方法authenticate和authenticate_header class BaseAuthentication: """ 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
SessionAuthentication认证器类代码
class SessionAuthentication(BaseAuthentication): """ Use Django's session framework for authentication. """ def authenticate(self, request): """ Returns a `User` if the request session currently has a logged in user. Otherwise returns `None`. """ # Get the session-based user from the underlying HttpRequest object user = getattr(request._request, 'user', None) # Unauthenticated, CSRF validation not required if not user or not user.is_active: return None self.enforce_csrf(request) # CSRF passed with authenticated user return (user, None) def enforce_csrf(self, request): """ Enforce CSRF validation for session based authentication. """ def dummy_get_response(request): # pragma: no cover return None check = CSRFCheck(dummy_get_response) # populates request.META['CSRF_COOKIE'], which is used in process_view() check.process_request(request) reason = check.process_view(request, None, (), {}) if reason: # CSRF failed, bail with explicit error message raise exceptions.PermissionDenied('CSRF Failed: %s' % reason)
所以我们要自定义认证类,只需要参考上面给出的代码,重写父类的两个方法即可,当然rest_framework/authentication.py这个文件里面还有BasicAuthentication,TokenAuthentication,RemoteUserAuthentication这个几个类的代码,也可以参考
自定义认证类
import logging from django.conf import settings from rest_framework.authentication import ( BaseAuthentication, get_authorization_header, ) from rest_framework.exceptions import AuthenticationFailed, PermissionDenied from six import raise_from from .managers.pam import auth from .models import User logger = logging.getLogger(__name__) class JWTAuthentication(BaseAuthentication): # 默认会返回Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b,指定keyword则会返回使用指定的关键字Authorization: Jwt 9944b09199c62bcf941 keyword = 'Jwt' # 自定义认证需要重写 authenticate 方法! def authenticate(self, request): # 由于 header 的头部是 byte 类型此方法 转换成 字符串 然后以空格为界 转换列表 # AUTHORIZATION這个就是之前服务端返回的,只不过Django会默认加上HTTP auth = get_authorization_header(request).split() # auth = ["Token", "c4840b5226a65806c586c239345fce66caf12409"] if not auth or auth[0].lower() != self.keyword.lower().encode(): return None if len(auth) == 1: raise AuthenticationFailed( detail='Invalid token header. No credentials provided.' ) elif len(auth) > 2: raise AuthenticationFailed( detail='Invalid token header.' 'Token string should not contain spaces.' ) try: token = auth[1].decode()# token = c4840b5226a65806c586c239345fce66caf12409 except UnicodeError: raise AuthenticationFailed( detail='Invalid token header.' 'Token string should not contain invalid characters.' ) return self.authenticate_credentials(token) # token 认证! def authenticate_credentials(self, token): import jwt from jwt import InvalidTokenError try: payload = jwt.decode( token, settings.SECRET_KEY, options={ 'verify_signature': True, 'verify_exp': True, 'verify_nbf': True, 'verify_iat': True, 'require_exp': True, 'require_nbf': True, 'require_iat': True, 'require_iss': True, 'require_jti': True, 'require_role': True, 'require_sub': True, 'require_mgt': True } ) # sub: jwt所面向的用户 user = User.objects.get(username=payload['sub']) payload_role = User.get_role_value(payload['role']) if payload_role > user.role: raise PermissionDenied( 'Insufficient permission.' ) return user, payload except InvalidTokenError as e: raise_from( AuthenticationFailed, e ) except User.DoesNotExist as e: raise_from( AuthenticationFailed, e ) # “jwt” 自定义的字符串 生成的token前面 def authenticate_header(self, request): return self.keyword class CookieAuthentication(JWTAuthentication): def authenticate(self, request): token = request.COOKIES.get('token') if token is None: return None token = token.decode() return self.authenticate_credentials(token)
视图类中使用
class view(APIView): authentication_classes = ( JWTAuthentication, CookieAuthentication )
settings.py配置文件中需要指定一下使用我们自定义的类路径
REST_FRAMEWORK = { # 认证器类 'DEFAULT_AUTHENTICATION_CLASSES': ( #'rest_framework.authentication.BasicAuthentication', # 基本认证 #'rest_framework.authentication.SessionAuthentication', # session认证 # 使用我们自定义的认证类 'antilles.user.plugins.JWTAuthentication' ), }
参考链接
https://www.cnblogs.com/shi-qi/articles/9629399.html https://www.cnblogs.com/jiakecong/p/14880244.html https://www.cnblogs.com/u-damowang1/p/13554143.html
__EOF__

本文链接:https://www.cnblogs.com/weiweivip666/p/15798418.html
关于博主:可能又在睡觉
版权声明:转载请注明出处
声援博主:如果看到我睡觉请喊我去学习
-------------------------------------------
个性签名:代码过万,键盘敲烂!!!
如果觉得这篇文章对你有小小的帮助的话,记得在右下角点个“推荐”哦,博主在此感谢!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人