准备
现有如下模板和视图:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<form action="/login/" method="post">
<p>用户名:<input type="text" name="username"></p>
<p>密码:<input type="text" name="password"></p>
<p><input type="submit" value="提交"></p>
<p style="color: red"> {{ msg }}</p>
</form>
</body>
</html>
login.html
from django.shortcuts import render,HttpResponse def login(request):
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
print(username, password)
return HttpResponse('登陆成功')
else:
return render(request, 'login.html')
views.py
使用
表单提交
使用上述模板中表单直接进行提交时,会发现会返回403错误如下:
这是因为Django中默认配置了一个拦截CSRF请求的中间件,在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',
]
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',
]setting.py
注释之后就可以正常提交请求了。
-
方案二:表单中添加csrf_token
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<form action="/login/" method="post">
{% csrf_token %}
<p>用户名:<input type="text" name="username"></p>
<p>密码:<input type="text" name="password"></p>
<p><input type="submit" value="提交"></p>
<p style="color: red"> {{ msg }}</p>
</form>
</body>
</html>login.html
在表单中添加上‘{%csrf_token%}’之后,查看网页源代码会发现表单中多了一个隐藏的输入框,如下:
Django通过这种方式让表单的请求带着token一起发送到服务器去验证。
Ajax请求
修改login.html内容如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录</title>
<script src="/static/jquery.min.js"></script>
</head>
<body>
<script>
function login() {
$.ajax({
url: "/login/",
type: "POST",
data: {"usr": "admin", "pwd": "123"},
{#headers:{ "X-CSRFtoken":$.cookie("csrftoken")},#}
success: function (data) {
alert(data)
}
})
}
login()
</script>
</body>
</html>
login.html
访问该页面会发现与表单请求一样被拦截:
-
方案一:headers中携带csrf_token
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录</title>
<script src="/static/jquery.min.js"></script>
</head>
<body>
<script>
function login() {
$.ajax({
url: "/login/",
type: "POST",
data: {"usr": "admin", "pwd": "123"},
headers: {
"X-CSRFtoken": '{{csrf_token}}'
},
success: function (data) {
alert(data)
}
})
}
login()
</script>
</body>
</html>login.html
-
方案二:data中携带csrf_token
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录</title>
<script src="/static/jquery.min.js"></script>
</head>
<body>
{% csrf_token %}
<script>
function login() {
$.ajax({
url: "/login/",
type: "POST",
data: {"usr": "admin", "pwd": "123", "csrfmiddlewaretoken": '{{csrf_token}}'},
success: function (data) {
alert(data)
}
})
} login()
</script>
</body>
</html>login.html
补充
全局添加csrf_token
如果页面中有多个ajax请求的话就可以通过下面方式在所有的ajax中添加headers信息:
$.ajaxSetup({
beforeSend: function (xhr, settings) {
xhr.setRequestHeader("X-CSRFtoken", '{{csrf_token}}')
}
});
这样就会在提交ajax之前执行这个方法,从而在所有的ajax里都加上这个csrftoken。
仅post提交添加csrf_token
如果想要实现在当get方式的时候不需要提交csrftoken,当post的时候需要,实现这种效果的代码如下:
function csrfSafeMethod(method) {
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
} $.ajaxSetup({
beforeSend: function (xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", '{{ csrf_token }}');
}
}
});
上述示例因csrf_token都是通过模板语言取出,所以html页必须由django的render函数渲染过。同理也可通过js取出cookie中csrf_token值,填充到对应位置即可。
指定视图函数不校验csrf
from django.views.decorators.csrf import csrf_exempt @csrf_exempt
def view_func(request):
pass