详解Django的CSRF认证
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())),
]
csrf_protect装饰器的用法跟上面一样
详解Django的CSRF认证的更多相关文章
- 详解Django rest_framework实现RESTful API
这篇文章主要介绍了详解Django rest_framework实现RESTful API,小编觉得挺不错的,现在分享给大家,也给大家做个参考.一起跟随小编过来看看吧 一.什么是REST 面向资源是R ...
- 这个贴子的内容值得好好学习--实例详解Django的 select_related 和 prefetch_related 函数对 QuerySet 查询的优化
感觉要DJANGO用得好,ORM必须要学好,不管理是内置的,还是第三方的ORM. 最最后还是要到SQL.....:( 这一关,慢慢练啦.. 实例详解Django的 select_related 和 p ...
- 程序员必备:详解XSS和CSRF
做开发的小伙伴想必都不陌生XSS 和 CSRF,但也有一些刚接触的朋友还不是很清楚,今天就给大家详解下XSS和CSRF! 一.XSS xss,即 Cross Site Script,中翻译是跨站脚本攻 ...
- 关于Django Ajax CSRF 认证
CSRF(Cross-site request forgery跨站请求伪造,也被称为“one click attack”或者session riding,通常缩写为CSRF或者XSRF,是一种对网站的 ...
- 转载 :实例详解Django的 select_related 和 prefetch_related 函数对 QuerySet 查询的优化(一)
在数据库有外键的时候,使用 select_related() 和 prefetch_related() 可以很好的减少数据库请求的次数,从而提高性能.本文通过一个简单的例子详解这两个函数的作用.虽然Q ...
- 详解Django的 select_related 和 prefetch_related 函数对 QuerySet 查询的优化
在数据库有外键的时候,使用 select_related() 和 prefetch_related() 可以很好的减少数据库请求的次数,从而提高性能.本文通过一个简单的例子详解这两个函数的作用. 1. ...
- 实例详解Django的 select_related
在数据库有外键的时候,使用 select_related() 和 prefetch_related() 可以很好的减少数据库请求的次数,从而提高性能.本文通过一个简单的例子详解这两个函数的作用.虽然Q ...
- vue-element-admin 模板 登录页面 post请求通过django的csrf认证,处理304错误
经过一天的研究,终于把 vue-admin-template 模板的 post 请求 和django的api 弄通了 没有了那该死的304报错了 直接贴代码: 在main.js中 我直接给设置了一个 ...
- python学习-- Django Ajax CSRF 认证
使用 jQuery 的 ajax 或者 post 之前 加入这个 js 代码:http://www.ziqiangxuetang.com/media/django/csrf.js /*======== ...
随机推荐
- nginx配置https双向验证(ca机构证书+自签证书)
nginx配置https双向验证 服务端验证(ca机构证书) 客户端验证(服务器自签证书) 本文用的阿里云签发的免费证书实验,下载nginx安装ssl,文件夹有两个文件 这两个文件用于做服务器http ...
- 2019-04-09 SpringBoot+Druid+MyBatis+Atomikos 的多数据源配置
前面部分是网上找的,我按照网上写的把自己搭建的过程展示一次 1.引入依赖 目前项目本来使用到了Mybatis plus(在自己的Mapper接口中继承BaseMapper获得基本的CRUD,而不需要增 ...
- 使用excel整理脚本
的时候需要通过excel数据初始化脚本,当数据过多的时候,脚本也就很多.这里记录一个平时用excel初始化脚本的小技巧. excel中在空单元格中写如下值: ="INSERT INTO db ...
- 【地图功能开发系列:一】根据当前坐标点获取距离不超过N公里的门店
在此处输入标题 声明变量 //假设当前坐标 double lon1 = 113.336028; double lat1 = 23.21745; //距离m double distance = 1000 ...
- es定期删除数据
es定期删除数据 1.定期删除索引 使用sentinl报警后,会产生大量如下索引,虽然不占空间,但时间久了也不好,故写个脚本定期删除 脚本如下: 1 #!/bin/bash 2 #只保留5天内的日志索 ...
- dedecms安装操作-重安装-开启GD库
wamp5_1.7.4 打开:http://localhost/install/安装 (输入:域名/install,回车跳转到安装界面,勾选“我已经阅读并同意此协议”,然后点击“继续按钮”.) 重安装 ...
- C语言博客05--指针
C语言博客05--指针 1.本章学习总结 1.1 思维导图 1.2 本章学习体会及代码量学习体会 1.2.1 学习体会 在本周的学习过程中,我们学习了指针的用法.说实话,指针的用法有点绕,之前一直没搞 ...
- Springboot集成Spring Batch
Spring官网 (https://spring.io/projects/spring-batch#overview)对Spring Batch的解释: 一个轻量级的.全面的批处理框架,用于开发对企 ...
- Java虚拟机内存溢出异常--《深入理解Java虚拟机》学习笔记及个人理解(三)
Java虚拟机内存溢出异常--<深入理解Java虚拟机>学习笔记及个人理解(三) 书上P39 1. 堆内存溢出 不断地创建对象, 而且保证创建的这些对象不会被回收即可(让GC Root可达 ...
- Docker配置镜像加速
1.获取镜像地址 1.1.阿里云 打开网址:https://cr.console.aliyun.com/#/accelerator 注册.登录.设置密码 然后在页面上可以看 ...