day21-CSRF
一、前言
之前我们写代码,都是会把settings配置文件里面的csrf的中间注释掉,不然我们请求post时候的会出现403 forbidden,那这个到底有啥用呐?它的原理又是什么呐?
csrf原理:
我们向服务请求时,服务器认为你是get请求的,服务器不仅把数据给你,还偷偷的的给你一大堆字符串,这个字符串是加密的,但是只有我服务器自己能反解,你客户端下一次提交数据的时候,需要带上这个字符串来再来,这样的话我服务器才会允许,不然的话,不允许你提交数据。
原理图:
二、From表单提交方式
2.1、前提条件
说明:因为我们之前都是注释掉django中的settings文件中的csrf插件,我们现在不用注释掉
'django.middleware.csrf.CsrfViewMiddleware' #全局中间件
2.2、templates中login.html的改进
说明:在输入框之前加上{% csrf_token %},表示这边加上一个隐藏的input框,这个input框默认值是csrf_token值,提交的发送给后端
<body> <form method="post" action="/login/"> {% csrf_token %} #会隐藏一个input框,这个input框的默认值为csrf_token的值 <input type="text" name="user" placeholder="用户名"> <input type="password" name="pwd" placeholder="密码"> <input type="checkbox" name="rmb" value="1">10秒免登录 <input type="submit" value="登录"> </form> </body>
如图:
确实会在添加一个隐藏的input的框,这个input乱的value是csrf_token的值。
2.3、get请求成cookie
说明:当我get请求的时候,我会自动在我的浏览器cookie生成一个值,然后相应的时候,服务器会返回这个cookie的值
所以,在input框之前添加{% csrf_toke %}的时候,不仅在form中生产一个隐藏的input框,而且请求get的时候,在本地cookie也会生产csrf的值,当然如果你在前端指向获取csfr_token的值,你就直接在前端获取就行了:
{{ csrf_token }}
三、ajax提交
我们之前说了,当我们需要用post提交的时候,我们需要带这个csrf_token去后台去跟服务器去校验,那我们怎么带这个这个csrf_token值呐,其实我们在请求的时候,把它放在header里面,发送给服务器就可以了。
3.1、ajax执行前置条件
说明:就是说对整个页面的ajax的做一个配置,如果是整体的ajax都要统一的加一个header,或者说统一的都是用post的请求方式,因为发送ajax之前都会执行以下这个函数,那么就可以用如下方式:
$.ajaxSetup({ beforeSend:function(xhr,settings){ xhr.setRequestHeader('X-CSRFtoken', $.cookie('csrftoken')); //xhr 表示是XMLHTTPResquest的一个对象,因为ajax的发送底层原理就是用这个的 //obj=XMLHTTPReuquest() //obj.open() //obj.send() //settings是下面的ajax的配置,比如settings.type,settings.url,settings.data等等 } });
3.2、ajax的请求方式
说明:所以我们就用完整的ajax请求一次
1 <body> 2 <form> 3 <input type="text" name="user" placeholder="用户名"> 4 <input type="password" name="pwd" placeholder="密码"> 5 <input type="checkbox" name="rmb" value="1">10秒免登录 6 <input type="submit" value="登录"> 7 <input id="btn" type="button" value="按钮"> #定义ajax请求的按钮 8 </form> 9 <script src="/static/jquery-1.12.4.js"></script> 10 <script src="/static/jquery.cookie.js"></script> 11 <script> 12 $(function(){ 13 var csrf_token = $.cookie('csrftoken'); #从cookie中获取csrftoken的值 14 $.ajaxSetup({ 15 beforeSend:function(xhr,settings){ 16 //这边可以写成X-CSRFTOKEN,但是官方建议写成X-CSRFtoken,记住了请求头信息不能用下划线,下划线是非法的,所以只能用扛 17 xhr.setRequestHeader('X-CSRFtoken', $.cookie('csrftoken')); 18 //settings是下面的ajax的配置 19 } 20 }); 21 22 $('#btn').click(function(){ 23 $.ajax({ 24 url:'/login/', 25 type:"POST", 26 data:{"user":"root",'pwd':'123'}, 27 //headers:{'X-CSRFtoken':csrf_token}, //写了ajaxSetup,这边就不用写了 28 success:function(arg){ 29 //location.href = '/index/' 30 } 31 }) 32 }) 33 }) 34 </script> 35 </body>
3.3、排除post请求
说明:如果我的ajax里面不是post请求是get请求的话,那咋办,它也会照样需要发送csrf_token,但是我们只想用post请求的时候,去校验这个csrf_token值:
function csrfSafeMethod(method) { // these HTTP methods do not require CSRF protection return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); }
所以我们完整的test.html编写如下:
1 <!DOCTYPE html> 2 <html> 3 <head lang="en"> 4 <meta charset="UTF-8"> 5 <title></title> 6 </head> 7 <body> 8 {% csrf_token %} 9 10 <input type="button" onclick="Do();" value="Do it"/> 11 12 <script src="/static/plugin/jquery/jquery-1.8.0.js"></script> 13 <script src="/static/plugin/jquery/jquery.cookie.js"></script> 14 <script type="text/javascript"> 15 var csrftoken = $.cookie('csrftoken'); 16 17 function csrfSafeMethod(method) { 18 // these HTTP methods do not require CSRF protection 19 return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); 20 } 21 $.ajaxSetup({ 22 beforeSend: function(xhr, settings) { 23 if (!csrfSafeMethod(settings.type) && !this.crossDomain) { 24 xhr.setRequestHeader("X-CSRFToken", csrftoken); 25 } 26 } 27 }); 28 function Do(){ 29 30 $.ajax({ 31 url:"/app01/test/", 32 data:{id:1}, 33 type:'POST', 34 success:function(data){ 35 console.log(data); 36 } 37 }); 38 39 } 40 </script> 41 </body> 42 </html>
更多:https://docs.djangoproject.com/en/dev/ref/csrf/#ajax
三、跨站请求伪造
django为用户实现防止跨站请求伪造的功能,通过中间件 django.middleware.csrf.CsrfViewMiddleware 来完成。而对于django中设置防跨站请求伪造功能有分为全局和局部。
3.1、全局
中间件 django.middleware.csrf.CsrfViewMiddleware
3.2、局部
说明:如果你100个函数,只有两个不用,那么就不用注释掉全局的,用csrf_exempt,如果说100个函数只用两个需要认证,就需要注释掉全局的,然后用csrf_protect。
局部:
- @csrf_protect,为当前函数强制设置防跨站请求伪造功能,即便settings中没有设置全局中间件。
- @csrf_exempt,取消当前函数防跨站请求伪造功能,即便settings中设置了全局中间件。
使用前提:from django.views.decorators.csrf import csrf_exempt,csrf_protect
3.3、具体使用如下:
from django.views.decorators.csrf import csrf_exempt,csrf_protect #这边需要导入这两个模块 @csrf_exempt #不需要认证 def index(request): if request.session.get("is_login",None): return render(request,'index.html') else: return HttpResponse('gun')