1. 基本应用
Django中通过生成随机字符串的方式对客户发送提交数据的请求(POST)进行验证,基本原理:客户端向服务端先发送GET请求,服务端会把随机字符串写到请求的页面上,客户端再来提交数据的时候需要带上这个随机字符串,服务端才会处理请求。默认中间件的配置中SCRF是开启的状态:
settings.py:
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware', # CSRF
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
views.py:
def csrf(request):
if request.method == 'GET':
return render(request, 'csrf.html')
else:
return HttpResponse('ok')
Django中的 {% csrf_token %}
就是服务端生成的随机字符串,如果在页面上没有加上它,那么请求的时候就不会带上这个随机字符串:
csrf.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="" method="post">
<input type="text">
<input type="submit">
</form>
</body>
</html>
在提交数据的时候会 403 Forbidden的错误:
如果在页面上加上 {% csrf_token %}
,客户端在发送GET请求后,服务端返回的页面上就会显示这个随机字符串,
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="" method="post">
<input type="text">
<input type="hidden" name="csrfmiddlewaretoken" value="a7brzXos3mDRZlmdWyJcKs72MJ4WtvXaZfa8xWUJq7E3dvNhFrWk3gesKdup3TR2">
<input type="submit">
</form>
</body>
</html>
客户端直接带着这个随机字符串提交提交数据即可,不会再包请求拒绝的问题。
{{ csrf_token }}会在页面上直接显示这个随机字符串,而{% csrf_token %}会生成input标签,这是有区别的。
2. 全站禁用
如果全站都不想使用CSRF的验证,可以全局禁用处理里CSRF,只需要在配置文件中的中间件部分注释掉CSRF的过滤,
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',
]
如果是这样,客户端提交数据请求时不会进行CSRF的处理。
3. 局部禁用
如果不想在某些页面提交请求数据时做CSRF验证,这个时候 django.middleware.csrf.CsrfViewMiddleware
中间件是(全局)开启的状态,那就可以通过 装饰器
的方式单独禁用这个页面发送提交数据请求的CSRF的验证,只需要 在视图函数中加入csrf_exempt装饰器
:
views.py:
from django.views.decorators.csrf import csrf_exempt
@csrf_exempt
def csrf(request):
if request.method == 'GET':
return render(request, 'csrf.html')
else:
return HttpResponse('ok')
4. 局部使用
如果在全局禁用的状态下,局部使用CSRF验证。需要在视图函数中加上 `` 装饰器,表示在这个视图函数对应的模板中提交数据的请求必须进行CSRF验证:
views.py:
from django.views.decorators.csrf import csrf_protect
@csrf_protect
def csrf(request):
if request.method == 'GET':
return render(request, 'csrf.html')
else:
return HttpResponse('ok')
如果在模板中没有加上 {% csrf_token%}
同样是请求被拒绝的:
加入csrf认证后服务端只接收来自本站提交数据的请求,别的站点发送的是不被接收的。所以,csrf验证是可以防止别的站点发送提交数据的请求到本站。但是,csrf认证不是完全可以避免的,如过是其它站点获取到了本站的csrf,带着它去请求本站,服务端也会认为是自己的站点发送的请求,会选择接收请求的。所以,csrf认证只是在一定程度上保护了站点。
5. AJAX携带CSRF提交数据
如果AJAX不携带CSRF字符串提交数据,也会出现 403 Forbidde 报错,所以 AJAX提交数据也是要携带CSRF字符串的
。AJAX携带CSRF提交数据的方法有两种,一种是把页面上的csrf随机字符串放在 data
中传到后端,另一种是放在 把csrf随机字符串放在请求头中
。首先探讨第一种方法:
views.py:
from django.views.decorators.csrf import csrf_protect
@csrf_protect
def csrf(request):
if request.method == 'GET':
return render(request, 'csrf.html')
else:
return HttpResponse('ok')
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form>
<input type="text" id="user">
{% csrf_token %}
<input type="button" value="提交" id="submit">
</form>
</body>
<script src="/static/js/jquery-3.5.1.min.js"></script>
<script>
$(function () {
submitForm();
});
function submitForm() {
$('#submit').click(function () {
var user = $('#user').val();
var csrfmiddlewaretoken = $('input[name="csrfmiddlewaretoken"]').val()
$.ajax({
url: '/index.html/', //可以不写,默认发送到本页面对应的视图函数中
type: 'post',
data: {
'user': user,
'csrfmiddlewaretoken': csrfmiddlewaretoken //csrfmiddlewaretoken也可以不获取直接拿"{{csrf_token}}"
},
success: function (arg) {
alert(arg) //ok
}
})
})
}
</script>
</html>
第二种方法,把cookie放在请求头中
。首先引入插件 jquery.cookie.js
,这个插件专门对cookie进行操作的,通过cokkie名可以直接获取cookie的值,也还可以设置 cookie:
Django规定从 浏览器获取的cookie
传入到Django中要传到 请求头
中,要使用到headers:
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form>
<input type="text" id="user">
<input type="button" value="提交" id="submit">
</form>
</body>
<script src="/static/js/jquery-3.5.1.min.js"></script>
<script src="/static/js/jquery.cookie.js"></script>
<script>
$(function () {
submitForm();
});
function submitForm() {
$('#submit').click(function () {
var user = $('#user').val();
var token = $.cookie('csrftoken');
$.ajax({
url: '/csrf.html/', //可以不写,默认发送到本页面对应的视图函数中
type: 'post',
headers:{'X-CSRFToken':token},
data: {
'user': user
},
success: function (arg) {
alert(arg) //ok
}
})
})
}
</script>
</html>