python框架之Django(9)-CSRF
准备
现有如下模板和视图:
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>登录</title> 6 </head> 7 <body> 8 <form action="/login/" method="post"> 9 <p>用户名:<input type="text" name="username"></p> 10 <p>密码:<input type="text" name="password"></p> 11 <p><input type="submit" value="提交"></p> 12 <p style="color: red"> {{ msg }}</p> 13 </form> 14 </body> 15 </html>
1 from django.shortcuts import render,HttpResponse 2 3 4 def login(request): 5 if request.method == 'POST': 6 username = request.POST.get('username') 7 password = request.POST.get('password') 8 print(username, password) 9 return HttpResponse('登陆成功') 10 else: 11 return render(request, 'login.html')
使用
表单提交
使用上述模板中表单直接进行提交时,会发现会返回403错误如下:
这是因为Django中默认配置了一个拦截CSRF请求的中间件,在settings.py中可配置:
1 MIDDLEWARE = [ 2 'django.middleware.security.SecurityMiddleware', 3 'django.contrib.sessions.middleware.SessionMiddleware', 4 'django.middleware.common.CommonMiddleware', 5 'django.middleware.csrf.CsrfViewMiddleware', # 此项便是拦截CSRF请求的中间件 6 'django.contrib.auth.middleware.AuthenticationMiddleware', 7 'django.contrib.messages.middleware.MessageMiddleware', 8 'django.middleware.clickjacking.XFrameOptionsMiddleware', 9 ]
-
方案一:去除该中间件(不推荐)
直接注释该行即可。
1 MIDDLEWARE = [ 2 'django.middleware.security.SecurityMiddleware', 3 'django.contrib.sessions.middleware.SessionMiddleware', 4 'django.middleware.common.CommonMiddleware', 5 # 'django.middleware.csrf.CsrfViewMiddleware', # 此项便是拦截CSRF请求的中间件 6 'django.contrib.auth.middleware.AuthenticationMiddleware', 7 'django.contrib.messages.middleware.MessageMiddleware', 8 'django.middleware.clickjacking.XFrameOptionsMiddleware', 9 ]
注释之后就可以正常提交请求了。
-
方案二:表单中添加csrf_token
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>登录</title> 6 </head> 7 <body> 8 <form action="/login/" method="post"> 9 {% csrf_token %} 10 <p>用户名:<input type="text" name="username"></p> 11 <p>密码:<input type="text" name="password"></p> 12 <p><input type="submit" value="提交"></p> 13 <p style="color: red"> {{ msg }}</p> 14 </form> 15 </body> 16 </html>
在表单中添加上‘{%csrf_token%}’之后,查看网页源代码会发现表单中多了一个隐藏的输入框,如下:
Django通过这种方式让表单的请求带着token一起发送到服务器去验证。
Ajax请求
修改login.html内容如下:
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>登录</title> 6 <script src="/static/jquery.min.js"></script> 7 </head> 8 <body> 9 <script> 10 function login() { 11 $.ajax({ 12 url: "/login/", 13 type: "POST", 14 data: {"usr": "admin", "pwd": "123"}, 15 {#headers:{ "X-CSRFtoken":$.cookie("csrftoken")},#} 16 success: function (data) { 17 alert(data) 18 } 19 }) 20 } 21 login() 22 </script> 23 </body> 24 </html>
访问该页面会发现与表单请求一样被拦截:
-
方案一:headers中携带csrf_token
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>登录</title> 6 <script src="/static/jquery.min.js"></script> 7 </head> 8 <body> 9 <script> 10 function login() { 11 $.ajax({ 12 url: "/login/", 13 type: "POST", 14 data: {"usr": "admin", "pwd": "123"}, 15 headers: { 16 "X-CSRFtoken": '{{csrf_token}}' 17 }, 18 success: function (data) { 19 alert(data) 20 } 21 }) 22 } 23 login() 24 </script> 25 </body> 26 </html>
-
方案二:data中携带csrf_token
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>登录</title> 6 <script src="/static/jquery.min.js"></script> 7 </head> 8 <body> 9 {% csrf_token %} 10 <script> 11 function login() { 12 $.ajax({ 13 url: "/login/", 14 type: "POST", 15 data: {"usr": "admin", "pwd": "123", "csrfmiddlewaretoken": '{{csrf_token}}'}, 16 success: function (data) { 17 alert(data) 18 } 19 }) 20 } 21 22 login() 23 </script> 24 </body> 25 </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
Java博客目录 | Python博客目录 | C#博客目录