django中间件&csrf跨站请求伪造
简介
之前了解过django请求生命周期流程图,中间件是django的门户:
- 所有来的请求都需要依次穿过中间件
- 所有走的响应也都需要依次穿过中间件
django默认有七个中间件,每个中间件负责的任务各不相同,并且django提供了接口,支持自定义中间件。
# django自带的七个中间件可以在配置文件中配置
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
自定义中间件
自定义方法
# 方法流程
1.在项目名或者应用名下创建一个任意名称的文件夹
2.在该文件夹内创建一个任意名称的py文件
3.在该py文件内需要书写类(这个类必须继承MiddlewareMixin)
- 然后在这个类里面自定义五个方法(需要几个定义几个)
4.最后需要将类的路径以字符串的形式注册到配置文件中才能生效
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'你自己写的中间件的路径1',
'你自己写的中间件的路径2',
'你自己写的中间件的路径3',
]
五个方法
请求来的时候穿过的方法:process_request
- 请求来的时候需要经过每一个中间件里面的
process_request
方法; - 结果的顺序是按照配置文件中注册的中间件从上往下的顺序依次执行;
- 如果中间件里面没有定义该方法,那么直接跳过执行下一个中间件;
- 如果该方法返回了
HttpResponse
对象,那么请求将不再继续往后执行而是直接原路返回;返回其他数据没有影响; process_request
方法就是用来做全局相关的所有限制功能。
from django.utils.deprecation import MiddlewareMixin
class MyMiddleWare(MiddlewareMixin):
def process_request(self, request):
print('我是第1个process_request')
process_request
方法返回了HttpResponse
对象:不再往下执行,直接原路返回。
响应走的时候穿过的方法:process_response
- 响应走的时候需要经过每一个中间件里面的
process_response
方法; - 该方法有两个参数
request
,response
; - 该方法必须返回一个
HttpResponse
对象:默认情况下返回的就是形参response
,你也可以自己返回被换成自己的; - 执行顺序是注册的中间件从下往上依次经过,如果你没有定义的话,直接跳过执行下一个(原路返回);
- 如果在第一个
process_request
方法就已经返回了HttpResponse
对象,则会直接走同级别的process_reponse
返回,不再执行后续其他中间件和视图函数了。
# 补充 flask框架也有一个中间件但是它的规律和django的不同, 只要返回数据了就必须经过所有中间件里面的类似于process_reponse方法
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse
class MyMiddleWare(MiddlewareMixin):
def process_response(self, request, response):
print('我是第1个process_response')
return response
# return HttpResponse('狸猫换太子')
其他三个了解方法:
# process_view
- 路由匹配成功之后执行视图函数之前,会自动执行中间件里面的process_view方法
- 顺序是按照配置文件中注册的中间件从上往下的顺序依次执行
- 形参view_name是请求url匹配到的视图函数
- 该方法要么不写返回值,要么返回值必须是一个HttpResponse对象
- 如果该方法返回了HttpResponse对象,那么请求将不再进入下一个中间件的process_view和视图函数,而是直接沿着原路径的process_response方法返回。
def process_view(self, request, view_name, *args, **kwargs):
print(view_name, args, kwargs)
print('我是第1个process_view')
# return HttpResponse('123')
# process_template_response
- 返回的HttpResponse对象有render属性的时候才会触发
- 顺序是按照配置文件中注册了的中间件从下往上依次经过,现在属于返回阶段了,方法都是从上往下执行。
def process_template_response(self, request, response):
print('我是第1个process_template_response')
return response
# views.py
def index(request):
print('index页面')
obj = HttpResponse('index')
def render():
print('内部的render')
return HttpResponse("O98K")
obj.render = render
return obj
# process_exception
- 当视图函数中出现异常的情况下触发
- 顺序是按照配置文件中注册了的中间件从下往上依次经过, 属于返回阶段了
def process_exception(self, request, exception):
print('我是第1个process_exception')
print(exception)
总结
-
两个掌握方法:
process_request
、process_response
-
三个了解方法:
process_view
、process_template_response
、process_exceptio
-
请求进来阶段:同级别方法按照中间件注册顺序从上往下依次执行
-
响应回去阶段:同级别方法按照中间件注册顺序从下往上依次执行
不严格的划分阶段:进入视图函数前属于请求阶段
-
遇到返回
HttpResponse
对象时,从同级别开始按照原路经返回,不再往视图函数的方向执行。
流程图解1:
process_view
方法
流程图解2:
process_view
CSRF跨站请求伪造
-
网站在给用户返回一个具有提交数据功能页面的时候会给这个页面加一个唯一标识;
-
当这个页面朝后端发送post请求的时候,后端会先校验唯一标识,校验通过后再正常执行。
-
这个唯一标识的目的就是防止非法网站伪造跨站请求,杜绝类似钓鱼网站的非法行为。
-
目前,提交post请求有两种:form表单和ajax请求
form表单校验
form表单内任意位置位置使用模版语法{% csrf_token %}
,这样在提交数据是携带唯一标识。
<form action="" method="post">
{% csrf_token %}
<p>username:<input type="text" name="username"></p>
<p>target_user:<input type="text" name="target_user"></p>
<p>money:<input type="text" name="money"></p>
<input type="submit">
</form>
ajax校验
方式1:直接手动添加键值对,但此时需要配合form表单中的 {% csrf_token %}
<script>
$('#btn').click(function () {
$.ajax({
url:'',
type:'post',
data:{
'money': $("input[name='money']").val(),
'csrfmiddlewaretoken': $("input[name='csrfmiddlewaretoken']").val(),
},
success:function (args) {
alert(args);
}
})
})
</script>
方式2:利用模版语法提供的快捷书写
data:{
'money': $("input[name='money']").val(),
'csrfmiddlewaretoken': '{{ csrf_token }}',
},
方式3:直接使用官网提供的js脚本,这种方式更通用适合前后端分离的项目
data:{
'money': $("input[name='money']").val(),
},
# html页面引用该js文件即可
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie !== '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
var csrftoken = getCookie('csrftoken');
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
beforeSend: function (xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
CSRF相关装饰器
# 需求:
1.网站整体都不校验csrf,就单单几个视图函数需要校验
2.网站整体都校验csrf,就单单几个视图函数不校验
# 办法:
- 使用django自带的装饰器csrf_protect, csrf_exempt分别解决上述两个需求
对于FBV视图函数:直接将装饰器加载需要的视图函数上即可
from django.views.decorators.csrf import csrf_protect,csrf_exempt
# @csrf_exempt
@csrf_protect
def transfer(request):
if request.method == 'POST':
username = request.POST.get('username')
target_user = request.POST.get('target_user')
money = request.POST.get('money')
print('%s给%s转了%s元'%(username, target_user, money))
return render(request, 'transfer.html')
对于CBV视图函数:借助method_decorator
from django.views.decorators.csrf import csrf_protect, csrf_exempt
from django.utils.decorators import method_decorator
class MyCsrfToken(View):
@method_decorator(csrf_exempt)
def dispatch(self, request, *args, **kwargs):
return super().dispatch(request, *args, **kwargs)
def get(self,request):
return HttpResponse('get')
def post(self,request):
return HttpResponse('post')
# CBV加装饰器有三种方式:方法上、类上、dispatch方法上
# 针对csrf_protect,三种方式都可以
# 针对csrf_exempt,只能加在dispatch方法上