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 : 以
分段上传数据(如果文件没有
,就一次性读取数据了,最好使用方式2方法)
        with open(file_obj.name, 'wb') as f: 
            #相对路径会从项目根目录开始
             for i in file_obj:  # 以
进行循环
                 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>
原文地址:https://www.cnblogs.com/jia-shu/p/14589836.html