10.Django -- csrf -- 文件上传
1.csrf
1.1 简介:
CSRF(Cross-site request forgery),中文名称:跨站请求伪造.
攻击者通过HTTP请求江数据传送到服务器,从而盗取回话的cookie。盗取回话cookie之后,攻击者不仅可以获取用户的信息,还可以修改该cookie关联的账户信息。
1.2 django的csrftoken防御机制
1.form 通过csrf认证:
settings.py中
MIDDLEWARE = [
# csrf 认证: 对所有的给后台发送的post和put请求进行了csrftoken认证
'django.middleware.csrf.CsrfViewMiddleware', #以后不用注释了
]
login.html
<body>
<form action="" method="post">
{% csrf_token %} # form表单中加入
# 浏览器解析为隐藏的input标签: <input type="hidden" name="csrfmiddlewaretoken" value="随机字符串">
用户名: <input type="text" name="username">
密码: <input type="password" name="password">
<input type="submit">
</form>
</body>
2.ajax通过csrf认证:
方式1: 主要是向后台发送token值
login.html --- body中添加{% csrf_token %} -- ajax中通过属性获取token值,data把token键值对提交到后台数据
<body>
{% csrf_token %} <!--添加csrf认证-->
用户名:<input type="text" id="username">
密码:<input type="password" id="pwd">
<span class="error"></span> <!--如果密码不对,用来存放错误信息-->
<button id="btn">ajax提交数据</button>
</body>
<script src="{% static 'jquery.js' %}"></script>
<script>
//ajax发送请求提交数据
$('#btn').click(function (){ //触发点击事件
var token = $('[name=csrfmiddlewaretoken]').val() //通过属性,获取csrf的token值
var user = $('#username').val() //通过id获取标签中数据
var pwd = $('#pwd').val()
$.ajax({
url:'/ajax_login/',//发送请求路径,往本网站发可以用相对路径
type:'post', //请求方法,post或get小写
data:{username:user,password:pwd,csrfmiddlewaretoken:token}, //向后台提交的数据,添加token键值对
// 2.当状态码为4xx(请求错误),5xx(服务端错误)等时,那么ajax会加工响应数据并传递给error对应的函数
success:function (res){
alert('登陆成功!!')
location.href = '/home/' //网页跳转到home路径,使用的浏览器的机制
},
error:function (error){ //后台返回更改4xx的状态码时,执行该语句
console.log('error>>>',error);
if (error.status === 400){
$('.error').text('用户名或密码有误!'); //向span标签添加错误信息
alert('用户名或密码有误')
}
},
})
})
</script>
方式2: 主要是向后台发送token值
$.ajax({
url:'/ajax_login/', // 请求路径 相对路径
type:'post', // 请求方法,小写
{#data:{a:1,b:2}, // 请求携带数据#}
data:{username:uname, password:pwd,csrfmiddlewaretoken:'{{csrf_token}}'},
} //{{}}直接在ajax发送值,不用提前获取值了
方式3:通过cookie完成认证
cookie中: csrftoken:asdf随机字符串
post请求数据: csrfmiddlewaretoken:asdf随机字符串
请求头键值对: X-CSRFToken:asdf随机字符串
认证顺序:
先认证请求数据部分,如果没有token数据,继续找请求头键值对,如果它与cookie中的csrftoken值相同,同样可以通过认证
下载引入jquery.cookie.js文件
百度bootcdn,Bootstrap 中文网开源项目免费 CDN 加速服务
搜索jquery.cookie,把代码复制到jquery.cookie.js中
HTML中引入:
<script src="{% static 'jquery.cookie.js' %}"></script>
取cookie值:$.cookie('csrftoken')
设置cookie值:$.cookie('xxx','ooo')
{% csrf_token %}
用户名:<input type="text" id="username">
密码:<input type="password" id="pwd">
<span class="error"></span> <!--如果密码不对,用来存放错误信息-->
<button id="btn">ajax提交数据</button>
</body>
<script src="{% static 'jquery.js' %}"></script>
<script src="{% static 'jquery.cookie.js' %}">
// 引入jquery.cookie.js文件
</script>
<script>
//ajax发送请求提交数据
$('#btn').click(function (){ //触发点击事件
{#var token = $('[name=csrfmiddlewaretoken]').val() //通过属性,获取csrf的token值#}
var user = $('#username').val() //通过id获取标签中数据
var pwd = $('#pwd').val()
$.ajax({
url:'/ajax_login/',//发送请求路径,往本网站发可以用相对路径
// url:'http://127.0.0.1:8000/ajax_login/',//往其他网站发只能用绝对路径
type:'post', //请求方法,post或get小写
{#data:{username:user,password:pwd,csrfmiddlewaretoken:token}, //向后台提交的数据#}
data:{username:user,password:pwd,}, //向后台提交的数据
header:{ //ajax加请求头键值对
"X-CSRFToken": $.cookie('csrftoken')//获取cookie中csrftoken值
},
// 2.当状态码为4xx(请求错误),5xx(服务端错误)等时,那么ajax会加工响应数据并传递给error对应的函数
success:function (res){
alert('登陆成功!!')
location.href = '/home/' //网页跳转到home路径,使用的浏览器的机制
},
error:function (error){ //后台返回更改4xx的状态码时,执行该语句
console.log('error>>>',error);
if (error.status === 400){
$('.error').text('用户名或密码有误!'); //向span标签添加错误信息
alert('用户名或密码有误')
}
},
})
})
2.上传文件
2.1 form表单上传文件
请求头键值对:
Content-Type: application/x-www-form-urlencoded
规定的是请求数据部分的数据格式: csrfmiddlewaretoken=V7TlcirtILCg70HhBmJwlBhhywOiQ9UYN2LLrrGN9TceOJQI37ysJFuPOjxHTHRl&uname=asdf&pwd=123
问题是文件数据不能通过这个格式发送,文件数据需要分片发送,换消息格式
Content-Type: multipart/form-data # 片段数据格式
更改文件上传格式:
<form action="" method="post" enctype="multipart/form-data">
路由:urls.py
from app01 import views
urlpatterns = [
url(r'^upload/', views.upload),
]
视图:views.py
def upload(request):
if request.method == 'GET':
return render(request, 'upload.html')
else:
print(request.POST)
#1. 没指定上传格式:
#<QueryDict: {'csrfmiddlewaretoken': ['随机字符串'], 'uname': ['chao'], 'pwd': ['123'], 'avatar': ['1.jpeg']}>
# 'avatar': ['1.jpeg'] -- 只是文件名称,没有文件数据
#2. 如果请求的数据格式是multipart/form-data,那么django会将文件数据单独处理,并且保存到request.FILES对象中
#<QueryDict: {'csrfmiddlewaretoken': ['随机字符串'], 'uname': ['chao'], 'pwd': ['123']}>
print(request.FILES)
#<MultiValueDict: {'avatar': [<InMemoryUploadedFile: 1.jpeg (image/jpeg)>]}>
# 内存文件句柄对象
file_obj = request.FILES.get('avatar')#获取文件对象
print(file_obj.name) # 获取文件名称
# 两种方式都可以上传文件
# 方式1 : 以\r\n分段上传数据(如果文件没有\r\n,就一次性读取数据了,最好使用方式2方法)
with open(file_obj.name, 'wb') as f:
#相对路径会从项目根目录开始
for i in file_obj: # 以\r\n进行循环
f.write(i)
# 方式2 自行控制一次性读取数据的大小
with open(file_obj.name, 'wb') as f:
for j in file_obj.chunks(): # 默认65536B
# for j in file_obj.chunks(chunk_size=数字): # 自己设置传输数据大小,或chunks(数字)也可以,就一个参数
f.write(j)
return render(request, 'upload.html')
templates目录下upload.html
<body>
<form action="" method="post" enctype="multipart/form-data"> <!--指定上传文件格式-->
{% csrf_token %}
用户名:<input type="text" name="uname">
密码:<input type="password" name="pwd">
头像:<input type="file" name="avatar">
<input type="submit">
</form>
</body>
2.2 ajax上传文件
路由urls.py和视图views.py和form表单上传文件一样.
templates目录下upload.html
{% load static %} <!--引入静态文件-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
用户名: <input type="text" name="uname" id="uname">
<br>
密码: <input type="text" name="pwd" id="pwd">
<br>
{# 头像: <input type="file" name="avatar" id="avatar" multiple>#} <!--multiple多选-->
头像: <input type="file" name="avatar" id="avatar">
<button id="btn">上传</button>
</body>
<script src="{% static 'jquery.js' %}"></script> <!--引入jquery文件-->
<script>
$('#btn').click(function () {
//获取input数据
var uname = $('#uname').val();
var pwd = $('#pwd').val();
var formdata = new FormData(); // ajax上传文件必须借助formdata对象
// FormData -- 将请求头键值对改成了content-type:mutiplepart/form-data
var file_obj = $('#avatar')[0].files[0]; // 获取的浏览器中上传的文件对象
//向formdata添加数据
formdata.append('uname', uname);
formdata.append('pwd', pwd);
formdata.append('avatar', file_obj);//文件对象名不变
formdata.append('csrfmiddlewaretoken', '{{ csrf_token }}'); # 通过scrf认证
$.ajax({
url:'/upload/',
type:'post',
data:formdata,
// ajax上传文件必须配置的两个参数
processData: false , // 不处理加工数据
contentType: false, // 不设置内容类型
success:function (res) {
console.log(res);
}
})
})
</script>
</html>