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
												
											drf中认证源码流程的更多相关文章
- restful framework 认证源码流程
		
一.请求到来之后,都要先执行dispatch方法,dispatch方法方法根据请求方式的不同触发get/post/put/delete等方法 注意,APIView中的dispatch方法有很多的功能 ...
 - Django后端项目----restful framework 认证源码流程
		
一.请求到来之后,都要先执行dispatch方法,dispatch方法方法根据请求方式的不同触发get/post/put/delete等方法 注意,APIView中的dispatch方法有很多的功能 ...
 - Django rest_framework 认证源码流程
		
一.请求到来后,都要先执行dispatch方法 dispatch根据请求方式的不同触发get/post/put/delete等方法 注意,APIView中的dispatch方法有很多的功能 def d ...
 - Rest Framework  认证源码流程
		
一.请求到来之后,都要先执行dispatch方法,dispatch方法方法根据请求方式的不同触发get/post/put/delete等方法 注意,APIView中的dispatch方法有很多的功能 ...
 - Django drf:序列化增删改查、局部与全局钩子源码流程、认证源码分析、执行流程
		
一.序列化类的增.删.改.查 用drf的序列化组件 -定义一个类继承class BookSerializer(serializers.Serializer): -写字段,如果不指定source ...
 - DRF  cbv源码分析  restful规范10条   drf:APIView的源码 Request的源码   postman的安装和使用
		
CBV 执行流程 路由配置:url(r'^test/',views.Test.as_view()), --> 根据路由匹配,一旦成功,会执行后面函数(request) --> 本质就是执 ...
 - Flask 源码流程,上下文管理
		
源码流程 创建对象 from flask import Flask """ 1 实例化对象 app """ app = Flask(__na ...
 - Django缓存机制--rest_framework中节流源码使用的就是django提供的缓存api
		
一.配置缓存 https://www.jb51.net/article/124434.htm 二.缓存全站.页面.局部 三.自我控制的简单缓存API API 接口为:django.core.c ...
 - Django session 源码流程
		
流程 Django session源码流程 首先执行的是SessionMiddleware的init方法 import_module(settings.SESSION_ENGINE) 导入了一个 dj ...
 - Shiro 登录认证源码详解
		
Shiro 登录认证源码详解 Apache Shiro 是一个强大且灵活的 Java 开源安全框架,拥有登录认证.授权管理.企业级会话管理和加密等功能,相比 Spring Security 来说要更加 ...
 
随机推荐
- [转帖]oom-killer错误排查过程
			
https://www.cnblogs.com/hphua/p/16395893.html 1.遇到的问题:应用在hi3536上跑一段不固定的时间,随之就会出现重启的现象:打印如下: app-run ...
 - sshpass 免密码进行文件复制的方法
			
1. 部分centos8 没有安装 sshpass 需要先安装 sshpass yum install sshpass 2. 需要增加一个配置文件, 避免因为 stickhost 检查 造成命令失效 ...
 - Grafana监控Redis的使用情况
			
Grafana监控Redis的使用情况 前言 最近在进行性能测试, 为了比较直观的将监控数据展示出来. 自己在周末时学习了下prometheus, 与之前的一个node_exporter一样, 本次进 ...
 - rel分支合并进入dev分支有冲突怎么处理?
			
rel分支合并进入dev分支有冲突怎么处理? 切换到本地rel 拉取远端rel 切换本地dev 拉去远端dev git merge rel 会出现冲突 解决后 推送到远端就可以
 - git查看自己是从那个分支建的分支
			
可能发生的情况 很多时候,开始建分支的时候, 能够确认自己是那个分支建的,但是当写完功能之后, 再去回想,有时是忘记自己基于那个分支建的分支. 这时有一个命令的话就可以很快的定位了. 查看创建的分支来 ...
 - 根据TxID获取上链数据
			
根据TxID获取上链信息 前段时间应甲方爸爸的要求,需要在现有的业务系统中新增一个根据TxID来查询上链信息的接口.搜了一圈发现相关的信息很少,最后只能祭出终极大招:Read Source Code. ...
 - SP5464 CT - Counting triangles 题解
			
题目翻译 题意 有一个网格,左上角是 \((0,0)\),右上角是 \((x,y)\).求这个网格中一共有多少个等腰直角三角形. 输入 第一行给定一个 \(c\),表示有 \(c\) 组数据. 后面 ...
 - 从零开始配置vim(21)——lsp简介与treesitter 配置
			
截止到上一篇文章,我们配置了neovim的很多内容了.具备了一些编辑器的常用功能了,而且可以胜任日常的文档编辑工作了.但是想作为一个可靠的代码编辑器还缺少重要的一环,即代码语法部分的支持. 在过去的v ...
 - 从零开始匹配vim(0)——vimscript 简介
			
通过之前一系列的文章,相信各位小伙伴应该已经对vim产生了浓厚的兴趣,可能不少小伙伴通过慢慢的使用变的跟我一样,离开vim就不会写代码了.如果你希望继续长时间使用vim,甚至将vim作为主要的代码编辑 ...
 - TienChin 活动管理-活动列表展示
			
后端 ActivityVO /** * @author BNTang * @version 1.0 * @description 活动管理VO * @since 2023-23-05 **/ publ ...