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中认证源码流程的更多相关文章

  1. restful framework 认证源码流程

    一.请求到来之后,都要先执行dispatch方法,dispatch方法方法根据请求方式的不同触发get/post/put/delete等方法 注意,APIView中的dispatch方法有很多的功能 ...

  2. Django后端项目----restful framework 认证源码流程

    一.请求到来之后,都要先执行dispatch方法,dispatch方法方法根据请求方式的不同触发get/post/put/delete等方法 注意,APIView中的dispatch方法有很多的功能 ...

  3. Django rest_framework 认证源码流程

    一.请求到来后,都要先执行dispatch方法 dispatch根据请求方式的不同触发get/post/put/delete等方法 注意,APIView中的dispatch方法有很多的功能 def d ...

  4. Rest Framework 认证源码流程

    一.请求到来之后,都要先执行dispatch方法,dispatch方法方法根据请求方式的不同触发get/post/put/delete等方法 注意,APIView中的dispatch方法有很多的功能 ...

  5. Django drf:序列化增删改查、局部与全局钩子源码流程、认证源码分析、执行流程

    一.序列化类的增.删.改.查 用drf的序列化组件   -定义一个类继承class BookSerializer(serializers.Serializer):   -写字段,如果不指定source ...

  6. DRF cbv源码分析 restful规范10条 drf:APIView的源码 Request的源码 postman的安装和使用

    CBV 执行流程 路由配置:url(r'^test/',views.Test.as_view()),  --> 根据路由匹配,一旦成功,会执行后面函数(request) --> 本质就是执 ...

  7. Flask 源码流程,上下文管理

    源码流程 创建对象 from flask import Flask """ 1 实例化对象 app """ app = Flask(__na ...

  8. Django缓存机制--rest_framework中节流源码使用的就是django提供的缓存api

    一.配置缓存   https://www.jb51.net/article/124434.htm 二.缓存全站.页面.局部   三.自我控制的简单缓存API API 接口为:django.core.c ...

  9. Django session 源码流程

    流程 Django session源码流程 首先执行的是SessionMiddleware的init方法 import_module(settings.SESSION_ENGINE) 导入了一个 dj ...

  10. Shiro 登录认证源码详解

    Shiro 登录认证源码详解 Apache Shiro 是一个强大且灵活的 Java 开源安全框架,拥有登录认证.授权管理.企业级会话管理和加密等功能,相比 Spring Security 来说要更加 ...

随机推荐

  1. Oracle的awr的学习与整理

    Oracle的awr的学习与整理 背景 本来想上周末进行一下总结和汇总 因为周末两天进行了一次长时间的培训.所以没有成行. 只能在工作之余找时间进行总结. 数据库部分自己一个不是很强. 其实也比较抗拒 ...

  2. 【转帖】Linux 调优篇 :虚拟化调优(irqbalance 网卡中断绑定)* 贰

    一.网络流量上不去二.中断绑定2.1 关闭中断平衡守护进程2.2 脱离中断平衡守护进程2.3 手动设置中断的CPU亲和性三. 总结 一.网络流量上不去 在Linux的网络调优方面,如果你发现网络流量上 ...

  3. [转帖]ansible小结(七)常用模块

    ansible小结(七)常用模块 http://www.361way.com/ansible-modules/4415.html   在上一篇中介绍了commands部分模块,本篇承接上篇介绍下常用的 ...

  4. [转帖]HTTP 框架 Hertz 实践入门:性能测试指南

    https://maimai.cn/article/detail?fid=1767401397&efid=R2_kM5y-yEUDCK88FZWrGA 干货不迷路2021 年 9 月 8 日, ...

  5. [转帖]Strace + pstack发现耗时点

    https://www.jianshu.com/p/10ea6fff562c 如何使用strace+pstack利器分析程序性能 本文摘抄自如何使用strace+pstack利器分析程序性能 程序说明 ...

  6. 麒麟信安V3.4 安装PG15的过程

    麒麟信安V3.4 安装PG15的过程 背景 发现基于OpenEuler的几个系统使用CentOS的rpm包 安装PG数据库时有问题. 会提示缺少依赖的so文件. 今天想着解决一下, 就百度了一下并且进 ...

  7. 你知道css3渐变吗线性渐变和径向渐变

    线性渐变 #app { width: 200px; height: 200px; background: linear-gradient(to bottom, red, green); /*从顶部到底 ...

  8. MySQL 存储过程与函数(精简笔记)

    MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,目前属于 Oracle 旗下产品.MySQL 是最流行的关系型数据库管理系统之一,在 WEB 应用方面,MySQL是最好的 RD ...

  9. C/C++ Qt 监控文件状态变化

    实现对特定文件的监控,Qt中提供了QFileSystemWatcher调用这个接口可以快速实现监控功能,当有文件发生变化是自动触发并输出文件具体信息. filesystem.h #ifndef FIL ...

  10. scrapy抓取校花网图片

    一:基础版(抓取首页图片) 爬虫py文件代码: 1 # -*- coding: utf-8 -*- 2 import scrapy 3 import sys 4 import io 5 from sc ...