1.csrf原理

csrf要求发送post,put或delete请求的时候,是先以get方式发送请求,服务端响应时会分配一个随机字符串给客户端,客户端第二次发送post,put或delete请求时携带上次分配的随机字符串到服务端进行校验

2.Django中的CSRF中间件

首先,我们知道Django中间件作用于整个项目。

在一个项目中,如果想对全局所有视图函数或视图类起作用时,就可以在中间件中实现,比如想实现用户登录判断,基于用户的权限管理(RBAC)等都可以在Django中间件中来进行操作

Django内置了很多中间件,其中之一就是CSRF中间件

MIDDLEWARE_CLASSES = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

上面第四个就是Django内置的CSRF中间件

3.Django中间件的执行流程

Django中间件中最多可以定义5个方法

process_request
process_response
process_view
process_exception
process_template_response

Django中间件的执行顺序

1.请求进入到Django后,会按中间件的注册顺序执行每个中间件中的process_request方法
如果所有的中间件的process_request方法都没有定义return语句,则进入路由映射,进行url匹配
否则直接执行return语句,返回响应给客户端
2.依次按顺序执行中间件中的process_view方法
如果某个中间件的process_view方法没有return语句,则根据第1步中匹配到的URL执行对应的视图函数或视图类
如果某个中间件的process_view方法中定义了return语句,则后面的视图函数或视图类不会执行,程序会直接返回
3.视图函数或视图类执行完成之后,会按照中间件的注册顺序逆序执行中间件中的process_response方法
如果中间件中定义了return语句,程序会正常执行,把视图函数或视图类的执行结果返回给客户端
否则程序会抛出异常
4.程序在视图函数或视图类的正常执行过程中
如果出现异常,则会执行按顺序执行中间件中的process_exception方法
否则process_exception方法不会执行
如果某个中间件的process_exception方法中定义了return语句,则后面的中间件中的process_exception方法不会继续执行了
5.如果视图函数或视图类中使用render方法来向客户端返回数据,则会触发中间件中的process_template_response方法

4.Django CSRF中间件的源码解析

Django CSRF中间件的源码

class CsrfViewMiddleware(MiddlewareMixin):

    def _accept(self, request):
request.csrf_processing_done = True
return None def _reject(self, request, reason):
logger.warning(
'Forbidden (%s): %s', reason, request.path,
extra={
'status_code': 403,
'request': request,
}
)
return _get_failure_view()(request, reason=reason) def _get_token(self, request):
if settings.CSRF_USE_SESSIONS:
try:
return request.session.get(CSRF_SESSION_KEY)
except AttributeError:
raise ImproperlyConfigured(
'CSRF_USE_SESSIONS is enabled, but request.session is not '
'set. SessionMiddleware must appear before CsrfViewMiddleware '
'in MIDDLEWARE%s.' % ('_CLASSES' if settings.MIDDLEWARE is None else '')
)
else:
try:
cookie_token = request.COOKIES[settings.CSRF_COOKIE_NAME]
except KeyError:
return None csrf_token = _sanitize_token(cookie_token)
if csrf_token != cookie_token:
# Cookie token needed to be replaced;
# the cookie needs to be reset.
request.csrf_cookie_needs_reset = True
return csrf_token def _set_token(self, request, response):
if settings.CSRF_USE_SESSIONS:
request.session[CSRF_SESSION_KEY] = request.META['CSRF_COOKIE']
else:
response.set_cookie(
settings.CSRF_COOKIE_NAME,
request.META['CSRF_COOKIE'],
max_age=settings.CSRF_COOKIE_AGE,
domain=settings.CSRF_COOKIE_DOMAIN,
path=settings.CSRF_COOKIE_PATH,
secure=settings.CSRF_COOKIE_SECURE,
httponly=settings.CSRF_COOKIE_HTTPONLY,
)
patch_vary_headers(response, ('Cookie',)) def process_request(self, request):
csrf_token = self._get_token(request)
if csrf_token is not None:
# Use same token next time.
request.META['CSRF_COOKIE'] = csrf_token def process_view(self, request, callback, callback_args, callback_kwargs):
if getattr(request, 'csrf_processing_done', False):
return None if getattr(callback, 'csrf_exempt', False):
return None if request.method not in ('GET', 'HEAD', 'OPTIONS', 'TRACE'):
if getattr(request, '_dont_enforce_csrf_checks', False):
return self._accept(request) if request.is_secure():
referer = force_text(
request.META.get('HTTP_REFERER'),
strings_only=True,
errors='replace'
)
if referer is None:
return self._reject(request, REASON_NO_REFERER) referer = urlparse(referer) if '' in (referer.scheme, referer.netloc):
return self._reject(request, REASON_MALFORMED_REFERER) if referer.scheme != 'https':
return self._reject(request, REASON_INSECURE_REFERER) good_referer = (
settings.SESSION_COOKIE_DOMAIN
if settings.CSRF_USE_SESSIONS
else settings.CSRF_COOKIE_DOMAIN
)
if good_referer is not None:
server_port = request.get_port()
if server_port not in ('443', '80'):
good_referer = '%s:%s' % (good_referer, server_port)
else:
good_referer = request.get_host() good_hosts = list(settings.CSRF_TRUSTED_ORIGINS)
good_hosts.append(good_referer) if not any(is_same_domain(referer.netloc, host) for host in good_hosts):
reason = REASON_BAD_REFERER % referer.geturl()
return self._reject(request, reason) csrf_token = request.META.get('CSRF_COOKIE')
if csrf_token is None:
return self._reject(request, REASON_NO_CSRF_COOKIE) request_csrf_token = ""
if request.method == "POST":
try:
request_csrf_token = request.POST.get('csrfmiddlewaretoken', '')
except IOError:
pass if request_csrf_token == "":
request_csrf_token = request.META.get(settings.CSRF_HEADER_NAME, '') request_csrf_token = _sanitize_token(request_csrf_token)
if not _compare_salted_tokens(request_csrf_token, csrf_token):
return self._reject(request, REASON_BAD_TOKEN) return self._accept(request) def process_response(self, request, response):
if not getattr(request, 'csrf_cookie_needs_reset', False):
if getattr(response, 'csrf_cookie_set', False):
return response if not request.META.get("CSRF_COOKIE_USED", False):
return response self._set_token(request, response)
response.csrf_cookie_set = True
return response

从上面的源码中可以看到,CsrfViewMiddleware中间件中定义了process_request,process_view和process_response三个方法

先来看process_request方法

def _get_token(self, request):
if settings.CSRF_USE_SESSIONS:
try:
return request.session.get(CSRF_SESSION_KEY)
except AttributeError:
raise ImproperlyConfigured(
'CSRF_USE_SESSIONS is enabled, but request.session is not '
'set. SessionMiddleware must appear before CsrfViewMiddleware ' 'in MIDDLEWARE%s.' % ('_CLASSES' if settings.MIDDLEWARE is None else '')
)
else:
try:
cookie_token = request.COOKIES[settings.CSRF_COOKIE_NAME]
except KeyError:
return None csrf_token = _sanitize_token(cookie_token)
if csrf_token != cookie_token:
# Cookie token needed to be replaced;
# the cookie needs to be reset. request.csrf_cookie_needs_reset = True
return csrf_token def process_request(self, request):
csrf_token = self._get_token(request)
if csrf_token is not None:
# Use same token next time.
request.META['CSRF_COOKIE'] = csrf_token

从Django项目配置文件夹中读取CSRF_USE_SESSIONS的值,如果获取成功,则从session中读取CSRF_SESSION_KEY的值,默认为'_csrftoken',如果没有获取到CSRF_USE_SESSIONS的值,则从发送过来的请求中获取CSRF_COOKIE_NAME的值,如果没有定义则返回None。

再来看process_view方法

在process_view方法中,先检查视图函数是否被csrf_exempt装饰器装饰,如果视图函数没有被csrf_exempt装饰器装饰,则程序继续执行,否则返回None。接着从request请求头中或者cookie中获取携带的token并进行验证,验证通过才会继续执行与URL匹配的视图函数,否则就返回403 Forbidden错误。

实际项目中,会在发送POST,PUT,DELETE,PATCH请求时,在提交的form表单中添加

{% csrf_token %}

即可,否则会出现403的错误

5.csrf_exempt装饰器和csrf_protect装饰器

5.1 基于Django FBV

在一个项目中,如果注册起用了CsrfViewMiddleware中间件,则项目中所有的视图函数和视图类在执行过程中都要进行CSRF验证。

此时想使某个视图函数或视图类不进行CSRF验证,则可以使用csrf_exempt装饰器装饰不想进行CSRF验证的视图函数

from django.views.decorators.csrf import csrf_exempt

@csrf_exempt
def index(request):
pass

也可以把csrf_exempt装饰器直接加在URL路由映射中,使某个视图函数不经过CSRF验证

from django.views.decorators.csrf import csrf_exempt  

from users import views  

urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^index/',csrf_exempt(views.index)),
]

同样的,如果在一个Django项目中,没有注册起用CsrfViewMiddleware中间件,但是想让某个视图函数进行CSRF验证,则可以使用csrf_protect装饰器

csrf_protect装饰器的用法跟csrf_exempt装饰器用法相同,都可以加上视图函数上方装饰视图函数或者在URL路由映射中直接装饰视图函数

from django.views.decorators.csrf import csrf_exempt  

@csrf_protect
def index(request):
pass

或者

from django.views.decorators.csrf import csrf_protect  

from users import views  

urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^index/',csrf_protect(views.index)),
]

5.1 基于Django CBV

上面的情况是基于Django FBV的,如果是基于Django CBV,则不可以直接加在视图类的视图函数中了

此时有三种方式来对Django CBV进行CSRF验证或者不进行CSRF验证

方法一,在视图类中定义dispatch方法,为dispatch方法加csrf_exempt装饰器

from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator class UserAuthView(View): @method_decorator(csrf_exempt)
def dispatch(self, request, *args, **kwargs):
return super(UserAuthView,self).dispatch(request,*args,**kwargs) def get(self,request,*args,**kwargs):
pass def post(self,request,*args,**kwargs):
pass def put(self,request,*args,**kwargs):
pass def delete(self,request,*args,**kwargs):
pass
方法二:为视图类上方添加装饰器
@method_decorator(csrf_exempt,name='dispatch')
class UserAuthView(View):
def get(self,request,*args,**kwargs):
pass def post(self,request,*args,**kwargs):
pass def put(self,request,*args,**kwargs):
pass def delete(self,request,*args,**kwargs):
pass

方式三:在url.py中为类添加装饰器

from django.views.decorators.csrf import csrf_exempt

urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^auth/', csrf_exempt(views.UserAuthView.as_view())),
]

源码解析:django的CSRF认证的更多相关文章

  1. 源码解析Django CBV的本质

    Django CBV模式的源码解析 通常来说,http请求的本质就是基于Socket Django的视图函数,可以基于FBV模式,也可以基于CBV模式. 基于FBV的模式就是在Django的路由映射表 ...

  2. 详解Django的CSRF认证

    1.csrf原理 csrf要求发送post,put或delete请求的时候,是先以get方式发送请求,服务端响应时会分配一个随机字符串给客户端,客户端第二次发送post,put或delete请求时携带 ...

  3. AspNetCore3.1_Secutiry源码解析_4_Authentication_JwtBear

    title: "AspNetCore3.1_Secutiry源码解析_4_Authentication_JwtBear" date: 2020-03-22T16:29:29+08: ...

  4. Django生命周期 URL ----> CBV 源码解析-------------- 及rest_framework APIView 源码流程解析

    一.一个请求来到Django 的生命周期   FBV 不讨论 CBV: 请求被代理转发到uwsgi: 开始Django的流程: 首先经过中间件process_request (session等) 然后 ...

  5. django之admin源码解析

    解析admin的源码 第一步:项目启动,加载settings文件中的 INSTALLED_APPS 里边有几个app就加载几个,按照注册顺序来执行. 第二步:其中加载的是admin.py,加载每一个a ...

  6. django -admin 源码解析

    admin源码解析 单例模式 单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在.当你希望在整个系统中,某个类只能出现一个实例时,单 ...

  7. Django框架 之 admin管理工具(源码解析)

    浏览目录 单例模式 admin执行流程 admin源码解析 单例模式 单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在.当你希望在 ...

  8. Django 之 admin组件使用&源码解析

    admin组件使用 Django 提供了基于 web 的管理工具. Django 自动管理工具是 django.contrib 的一部分.可以在项目的 settings.py 中的 INSTALLED ...

  9. .Net Core 认证系统之Cookie认证源码解析

    接着上文.Net Core 认证系统源码解析,Cookie认证算是常用的认证模式,但是目前主流都是前后端分离,有点鸡肋但是,不考虑移动端的站点或者纯管理后台网站可以使用这种认证方式.注意:基于浏览器且 ...

  10. .Net Core 认证系统之基于Identity Server4 Token的JwtToken认证源码解析

    介绍JwtToken认证之前,必须要掌握.Net Core认证系统的核心原理,如果你还不了解,请参考.Net Core 认证组件源码解析,且必须对jwt有基本的了解,如果不知道,请百度.最重要的是你还 ...

随机推荐

  1. 第一章C语言概述

    1.1程序实例 //first.程序 #include <stdio.h> int main() { int num; num = 1; printf("I am a simpl ...

  2. Redis与Kafka的区别

    第一: Kafka与Redis PUB/SUB之间较大的区别在于Kafka是一个完整的系统,而Redis PUB/SUB只是一个套件(utility)--没有冒犯Redis的意思,毕竟它的主要功能并不 ...

  3. 获得New Bing资格后,在Ubuntu环境下使用New Bing

    技术背景 如今基于GPT-4的New Bing,结合搜索引擎的功能,可以说已经达到了非常高的智力水平.虽说ChatGPT的出现打击了很多的行业,但是对我们来说也未必不是一种机遇.合理的使用ChatGP ...

  4. Nacos 实现 AP+CP原理[Raft 算法 NO]

    来源于网络 一.什么是 Raft算法 Raft 适用于一个管理日志一致性的协议,相比于 Paxos 协议 Raft 更易于理解和去实现它.为了提高理解性,Raft 将一致性算法分为了几个部分,包括领导 ...

  5. js循环中reduce的用法简单介绍

    reduce() 方法接收一个函数作为累加器,reduce 为数组中的每一个元素依次执行回调函数,不包括数组中被删除或从未被赋值的元素,接受四个参数:初始值(上一次回调的返回值),当前元素值,当前索引 ...

  6. Perceptron, Support Vector Machine and Dual Optimization Problem (3)

    Support Vector Machines Perceptron and Linear Separability 假设存在一个 linear decision boundary,它可以完美地对 t ...

  7. 线性规划的单纯形法—R实现

    table { margin: auto } 线性规划的单纯形法 线性规划是运筹学中的一个基本分支,它广泛应用现有的科学技术和数学方法,解决实际中的问题,帮助决策人员选择最优方针和决策,自1947年丹 ...

  8. TypeScript 学习笔记 — 自定义类型:部分属性可选,反选 key,求对象交差并补集等(十三)

    目录 将部分属性变为可选属性 根据值的类型 反选 key 写法一:基础原理写法,使用不同的内置类型,Pick 和 Omit 写法二:基础原理写法,使用 Pick 内置类型 + 传参的方式 写法三:使用 ...

  9. pysimplegui之运行多个窗口

    运行多个窗口 这就是 PySimpleGUI 继续简单的地方,但问题空间刚刚进入"复杂"领域. 如果您希望在事件循环中运行多个窗口,那么有两种方法可以做到这一点. 当第二个窗口可见 ...

  10. python之PySimpleGUI(二)属性

    属性 Size• Key 相当于句柄/ID• Font• Pad• Colors• Enable Events• Visibility• Tooltip• Metadata• Right click ...