day56:django:csrf_token&文件上传
目录
csrf介绍
什么是csrf?
csrf:跨站请求伪造。攻击者通过HTTP请求将数据传送到服务器,从而盗取会话的cookie。
盗取会话cookie之后,攻击者不仅可以获取用户的信息,还可以修改该cookie关联的账户信息。
django实现csrf_token认证
1.form表单实现csrf_token认证
直接写上{% csrf_token %}
<form action=""> {% csrf_token %} <input type="text" name="username"> <input type="submit"> </form>
2.ajax实现csrf_token认证
方式1
在html里,写了个{% csrf token %},其实翻译过来,就是生成了个input标签
input标签是这个样子的:
<input type="hidden" name="csrfmiddlewaretoken" value="BzOzpsnD1zqiZMm3jp1TdW2knEI2BvxwEXrsJbZTIdqL1Kj6P7o4pN38sTn8K5ia">
我们在js中获取属性name为csrfmiddlewaretokan的那个input标签的value的值
然后通过ajax中的data传递过去
// 方式1 <input type="text" name="username"> <button id="btn">确认</button> {% csrf_token %} <script src="{% static 'plugins/jquery.js' %}"></script> <script> var uname = $('#username').val(); // 方式1 // 通过input标签的name属性获取csrfmiddlewaretoken的值 var token = $('[name="csrfmiddlewaretoken"]').val(); $.ajax({ url:'/ajax_login', type:'post', data:{uname:uname,csrfmiddlewaretoken:token} success:function (res) { alert(res) } }) </script>
方式2
这种方式和方式1差不多,只是在data中csrfmiddlewaretoken的值 变成了{{ csrf_token }}
在django中可以通过双大括号取值
<input type="text" name="username"> <button id="btn">确认</button> {% csrf_token %} <script src="{% static 'plugins/jquery.js' %}"></script> <script> var uname = $('#username').val(); $.ajax({ url:'/ajax_login', type:'post', // 方式2 // 直接拿到csrfmiddlewaretoken中value属性对应的值 data:{uname:uname,csrfmiddlewaretoken:'{{ csrf_token }}'}, success:function (res) { alert(res) } }) </script>
方式3
data里不在写csrfmiddlewaretoken,而是写在ajax中的header里
这种方法的csrf认证是写在cookie里的 cookie里的csrf和input标签里value的csrf是不一样的
<input type="text" name="username"> <button id="btn">确认</button> {% csrf_token %} <script src="{% static 'plugins/jquery.js' %}"></script> <script> var uname = $('#username').val(); $.ajax({ url:'/ajax_login', type:'post', data:{uname:uname}, headers:{ 'X-CSRFToken':$.cookie('csrftoken') // 这个csrftoken是cookie中的csrftoken,并非csrfmiddlewaretoken的值,两者本身是同一个之,但是二者的加密方式不同,所以加密出来的结果不相同 }, success:function (res) { alert(res) } }) </script>
3.关于csrf自己的一些理解
csrf
''' 1.本质 我们在钓鱼网站的页面 针对对方用户 只给用户提供一个没有name属性的普通input框 然后我们在内部隐藏一个已经写好的name和value的input框 2.如何规避跨站攻击问题 csrf跨站请求伪造校验 网站在给用户返回一个具有提交数据的页面的时候会给这个页面加一个唯一标识 当这个页面朝后端发送post请求的时候,后端会先校验唯一标识 如果唯一标识不对,直接拒绝(403 forbidden) 如果成功则正常执行 两个网站请求两个页面,两个网站分别返回两个页面,两个页面长的一模一样 当页面提交post请求的时候 **:网站在返回页面的时候,给每个页面都携带了一个随机标识过去的 两个网站请求两个页面 客户端向服务端发送请求 服务端返回页面给客户端,在返回页面的时候是带了一个随机标记过去的 比如说正常网站请求页面,网站返回一个页面,并携带随机标识33333 钓鱼网站请求页面,网站返回一个页面,并携带随机标识44444(因为标识唯一,所以两个标识绝对不相同) 客户端(浏览器)有input框,输入数据提交,这个时候客户端向服务端发送POST请求 服务端会验证你发过来的post请求里,有没有唯一标记33333 而钓鱼网址他的唯一标识,是钓鱼网址的服务端给它发的44444 而当正常网址/钓鱼网址提交post请求时,服务端会验证你的唯一标识是否是他之前给客户端的那个 如果唯一标识正确,就可以向后台提交数据 如果唯一标识错误,就会把它forbidden 403掉,不能向后台提交数据 '''
csrf_token
# form表单如何符合校验 在form表单里面添加{% csrf_token %} ''' 当客户端向服务端发送请求时,{% csrf_token %}会解析成一个input标签 这个input标签携带了那个唯一标识 当我点击提交按钮的时候,这个唯一标识会被提交到后端 '''
# ajax如何符合校验 ''' 第一种:利用标签查找获取页面上的随机字符串 在ajax的data中 键值必须叫csrfmiddlewaretoken 值通过属性选择器去获取name=csrfmiddlwaretoken的input标签的value值,也就是那个唯一标识串 data:{uname:uname,csrfmiddlewaretoken:$('[name="csrfmiddlewaretoken"]').val()} 第二种:直接利用模板语法提供的快捷书写 {{ }}语法来写 data:{uname:uname,csrfmiddlewaretoken:'{{ csrf_token }}'}, 第三种:写在header里 headers:{ 'X-CSRFToken':$.cookie('csrftoken') 第四种:引入一个js文件,其他的都不用写了 '''
django实现文件上传
1.form表单实现文件上传
<form action="" method="post" enctype="multipart/form-data"> {% csrf_token %} 用户名:<input type="text" name="username" > <!-- type类型设置为file,multiple代表可以上传多个文件 --> 头像:<input type="file" name="avatar" multiple> <input type="submit"> </form>
2.ajax实现文件上传
从html获取数据,在js中获取定义成变量
然后放到formdata中
在ajax中的data直接传formdata即可,注意加processData和ContentType
用户名:<input type="text" name="username" id="username"> 头像:<input type="file" name="avatar" id="avatar"> <button id="ajax_btn">上传</button> <script> $('#ajax_btn').click(function () { var uname = $('#username').val(); var file_obj = $('#avatar')[0].files[0]; // 文件对象 var formdata = new FormData(); // 创建formdata对象,用来获取表单数据,方便进行提交数据 formdata.append('username',uname); // 获取input框输入的用户名 formdata.append('csrfmiddlewaretoken','{{ csrf_token }}'); // 获取csrf_token值 formdata.append('avatar',file_obj); // 获取用户上传的文件对象 $.ajax({ url:'/login/', type:'post', data:formdata, processData: false , // 不处理数据 contentType: false, // 不设置内容类型 success:function (res) { console.log(res); } }) }) </script>
3.文件上传的views.py(form表单和ajax都用这套视图函数)
通过request读取在前端js定义好的file_obj
然后通过文件对象.name 取出文件名 为接下来with open操作做准备
with open(文件名....)打开文件
for循环文件对象 将文件内容写入其他地方
def login(request): if request.method == 'GET': return render(request, 'login.html') else: file_obj = request.FILES.get('avatar') name = file_obj.name with open(name, 'wb') as f: # 方式1 # for i in file_obj: # \r\n
# 方式2 # for i in file_obj.chunks(): # \r\n for i in file_obj.chunks(): # \r\n 读取65536B f.write(i) return HttpResponse('ok')