【问题标题】:CSRF token missing on file upload ajax request文件上传 ajax 请求中缺少 CSRF 令牌
【发布时间】:2015-04-08 16:52:51
【问题描述】:

我有一个将文件发送到 django 的 ajax 请求,它说 csrf 令牌丢失,但我复制了我的其他正在工作的 ajax 请求。我确定这与尝试传递要上传的文件有关。

我收到 403 和 csrf 缺失返回。

base.html

<script type='text/javascript' src='http://code.jquery.com/jquery-1.8.2.js'></script>
<script type="text/javascript">
    $(document).ready(function() {  
                $("#create_token").click(function() {
                        var username = document.getElementById("username").value;
                        $.ajax({
                            url : "/gettoken/", 
                            type : "POST",
                            dataType: "json", 
                            data : {
                                csrfmiddlewaretoken: '{{ csrf_token }}',
                                create_token: 'create_token',
                                username: username,
                                },
                                success : function(json) {
                                    document.getElementById('output').innerHTML = ('Token: ' + json['token']);
                                },
                                error : function(xhr,errmsg,err) {
                                    console.log(xhr.status + ": " + xhr.responseText);
                                    document.getElementById('output').innerHTML = "Token:" + " Request Failed.";
                                }

                        });
                        return false;  
                }); 

                $("#add_friend").click(function() {
                        var token = document.getElementById("friend_token").value;
                        $.ajax({
                            url : "/addfriend/", 
                            type : "POST",
                            dataType: "json", 
                            data : {
                                csrfmiddlewaretoken: '{{ csrf_token }}',
                                add_friend: token,
                                },
                                success : function(json) {
                                    document.getElementById('output').innerHTML = (json['message']);
                                },
                                error : function(xhr,errmsg,err) {
                                    console.log(xhr.status + ": " + xhr.responseText);
                                    document.getElementById('output').innerHTML = "Request Failed.";
                                }

                        });
                        return false;  
                }); 

                $("#uppropic").click(function() {
                        var file = document.getElementById("profile_pic").files[0];
                        console.log(file);
                        $.ajax({
                            url : "profilepic/", 
                            type : "POST",
                            dataType: "json", 
                            processData: false,
                            data : {
                                csrfmiddlewaretoken: '{{ csrf_token }}',
                                profile_pic: file,
                                },
                                success : function(json) {
                                    document.getElementById('output').innerHTML = (json['message']);
                                },
                                error : function(xhr,errmsg,err) {
                                    console.log(xhr.status + ": " + xhr.responseText);
                                    document.getElementById('output').innerHTML = " Request Failed.";
                                }

                        });
                        return false;  
                }); 


            });

home.html

{% extends "base.html" %}
{% block title %}
{% for user in user_data %}
    {{user.username}}
{%endfor%}
{% endblock %}
{% block content %}
    {% for user in user_data %}
        Username: {{user.username}}<br>
        First Name: {{user.first_name}}<br>
        Last Name: {{user.last_name}}<br>
        About: {{user.about}}<br>
        Title: {{user.title}}<br>
    {%endfor%}
    Friends:
    {% for friend in friend_data %}
        {{friend}}<br>
    {%endfor%}
            {% if is_user_profile %}
        <form method='POST' >
        {% csrf_token %}
        <input type='text' name='friend_token' id='friend_token'>
        <button id='add_friend' name = 'add_friend' value='add_friend' > Add Friend </button>
        </form>

        <form method='POST' >
        {% csrf_token %}
        <button id='create_token' name = 'create_token' value='create_token' > Create Token </button>
        {% for user in user_data %}
        <input type='hidden' id='username' value='{{user.username}}'>
        {%endfor%}
        </form>

        <p id='output'>
        </p>
        {%endif%}

        <form method='POST'>
        {% csrf_token %}
        <input type='file' name='profile_pic' id='profile_pic'>
        <button id='uppropic'> Upload Profile Pic</button>
        {% for user in user_data %}
        <input type='hidden' id='username' value='{{user.username}}'>
        {%endfor%}
        </form>

    <a href="/logout/">Logout</a>
{% endblock %}

views.py

@login_required  
@csrf_protect       
def upload_profilepic(request):
    context = {}
    if request.method == 'POST':
        post_data = request.POST.copy()
        profile_pic = post_data['profile_pic']
        print post_data, profile_pic
        handle_uploaded_file(request.FILES['file'])
        context.update({'message':'You must select a file to upload.'})
        return HttpResponse(simplejson.dumps(context), content_type='application/json')   
    else:
        context.update({'message':'You must select a file to upload.'})
        return HttpResponse(simplejson.dumps(context), content_type='application/json')   

控制台输出

File { name: "10923328_1112855418728180_5377511192406844214_n.png", lastModified: 1421116207673, lastModifiedDate: Date 2015-01-13T02:30:07.673Z, size: 664332, type: "image/png" } home:56
"403: 
<!DOCTYPE html>
<html lang="en">
<head>
  <meta http-equiv="content-type" content="text/html; charset=utf-8">
  <meta name="robots" content="NONE,NOARCHIVE">
  <title>403 Forbidden</title>
  <style type="text/css">
    html * { padding:0; margin:0; }
    body * { padding:10px 20px; }
    body * * { padding:0; }
    body { font:small sans-serif; background:#eee; }
    body>div { border-bottom:1px solid #ddd; }
    h1 { font-weight:normal; margin-bottom:.4em; }
    h1 span { font-size:60%; color:#666; font-weight:normal; }
    #info { background:#f6f6f6; }
    #info ul { margin: 0.5em 4em; }
    #info p, #summary p { padding-top:10px; }
    #summary { background: #ffc; }
    #explanation { background:#eee; border-bottom: 0px none; }
  </style>
</head>
<body>
<div id="summary">
  <h1>Forbidden <span>(403)</span></h1>
  <p>CSRF verification failed. Request aborted.</p>


</div>

<div id="info">
  <h2>Help</h2>

    <p>Reason given for failure:</p>
    <pre>
    CSRF token missing or incorrect.
    </pre>


  <p>In general, this can occur when there is a genuine Cross Site Request Forgery, or when
  <a
  href='http://docs.djangoproject.com/en/dev/ref/contrib/csrf/#ref-contrib-csrf'>Django's
  CSRF mechanism</a> has not been used correctly.  For POST forms, you need to
  ensure:</p>

  <ul>
    <li>Your browser is accepting cookies.</li>

    <li>The view function uses <a
    href='http://docs.djangoproject.com/en/dev/ref/templates/api/#subclassing-context-requestcontext'><code>RequestContext</code></a>
    for the template, instead of <code>Context</code>.</li>

    <li>In the template, there is a <code>{% csrf_token
    %}</code> template tag inside each POST form that
    targets an internal URL.</li>

    <li>If you are not using <code>CsrfViewMiddleware</code>, then you must use
    <code>csrf_protect</code> on any views that use the <code>csrf_token</code>
    template tag, as well as those that accept the POST data.</li>

  </ul>

  <p>You're seeing the help section of this page because you have <code>DEBUG =
  True</code> in your Django settings file. Change that to <code>False</code>,
  and only the initial error message will be displayed.  </p>

  <p>You can customize this page using the CSRF_FAILURE_VIEW setting.</p>
</div>

</body>
</html>
"

在做了一些研究之后,问题与 ajax 请求将文件传递给 django 的方式有关,这导致了我的 csrf 错误。所以我尝试通过将 csrf 令牌附加到 formData 来跟随其他人所做的事情。

这是一个包含JQuery: post FormData AND csrf token together的堆栈帖子

以下设置弄乱了 ajax 请求 处理数据:假, 内容类型:假,

            $("#uppropic").click(function() {
                    var file = document.getElementById("profile_pic").files[0];
                    console.log(file);
                    var csrf = '{{ csrf_token }}';
                    console.log(csrf);
                    var formData = new FormData(document.getElementById("profile_pic_form"));
                    formData.append('csrfmiddlewaretoken', csrf );
                    console.log(formData);

                    $.ajax({
                        url : "/profilepic/", 
                        type : "POST",
                        dataType: "json", 
                        processData: false,
                        contentType: false,
                        data : {
                            csrfmiddlewaretoken: '{{ csrf_token }}',
                            profile_pic: file,
                            },
                            success : function(json) {
                                document.getElementById('output').innerHTML = (json['message']);
                            },
                            error : function(xhr,errmsg,err) {
                                console.log(xhr.status + ": " + xhr.responseText);
                                document.getElementById('output').innerHTML = " Request Failed.";
                            }

                    });
                    return false;  
            }); 

【问题讨论】:

  • 将服务器端模板标签混合到客户端 JavaScript 中通常不是一个好主意。 {{ ... }} 之类的东西应该在 html 中,或者如果你绝对必须,你应该把 var csrf = '{{ csrf_token }}'; 行放在 的脚本标签中,其余的 JS 应该放在没有任何模板标签的 .js 文件中.这不会解决您的问题,但会帮助您调试,因为您可以“查看源代码”并查看标记中的内容。

标签: jquery python ajax django


【解决方案1】:

通常我在模板未设置csrf_token 时看到此错误。来自docs

如果您的视图未呈现包含 csrf_token 模板标签的模板,则 Django 可能不会设置 CSRF 令牌 cookie。这在表单被动态添加到页面的情况下很常见。为了解决这种情况,Django 提供了一个视图装饰器来强制设置 cookie:ensure_csrf_cookie()

渲染文件上传表单的页面是否设置了token?也许{% if is_user_profile %} 条件评估为False 并且没有设置令牌?

如果没有其他帮助,您可以尝试在视图中使用 @ensure_csrf_cookie 装饰器来呈现文件上传表单。

【讨论】:

  • 如果 is_user_profile 真的是不必要的,我只是确保只有当用户在他们的个人资料上时,他们才能看到发布和添加朋友的选项。我正在使用一种独特的方式来添加我不会讨论的朋友,但这只是我通过的一个变量。问题是此页面上还有其他三种形式,它们正在工作。他们工作并完美地传递了ajax请求。由于我对文件做了什么,所以没有用于文件上传的表单,我通过 ajax 请求将它传递给views.py,然后我保存文件。文件位置保存在该用户的数据库中。
【解决方案2】:

我通过将以下内容添加到我的头部解决了这个问题:

<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
<sec:csrfMetaTags />
<script type="text/javascript">
  var csrfParameter = $("meta[name='_csrf_parameter']").attr("content");
  var csrfHeader = $("meta[name='_csrf_header']").attr("content");
  var csrfToken = $("meta[name='_csrf']").attr("content");
</script>

请求的数据如下所示:

var file = document.getElementById("rawdatafile");
var completeData = new FormData();
completeData.append("rawData", file.files[0]);

在 Ajax-Request 中我做了:

$.ajax({
  type: "POST",
  url: "<c:url value='/myurl/etc' />",
  headers: {'X-CSRF-TOKEN': csrfToken},
  cache: false,
  processData: false,
  contentType: false,
  data: completeData,
  dataType: "text",
  ...

这样我可以上传一个多部分文件,还可以通过 http 标头传递 csrf 令牌。

【讨论】:

    【解决方案3】:

    不要使用 JSON 之类的数据。看起来像:

    $.ajax({
        data: {
            csrfmiddlewaretoken:'{{ csrf_token }}',
            profile_pic: file
        },
    })
    

    使用JS方法new FormData()创建数据对象。看起来像:

    $.ajax({
        data: new FormData($('form')[0]),
    })
    

    或者:

    var formData = new FormData()
    formData.append("csrfmiddlewaretoken", '{{ csrf_token }}')
    formData.append("profile_pic", file)
    
    $.ajax({
        data: formData,
    })
    

    完整的 AJAX 示例:

       $.ajax({
           type : 'post',
           url : "profilepic/",
           data : formData,
    
           // You *must* include these options!
           cache : false,
           processData : false, 
           contentType : false, 
    
           success : function(){}
           error : function(){ }
       })
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-06-01
      • 2021-12-28
      • 2017-12-19
      • 1970-01-01
      • 1970-01-01
      • 2015-07-02
      • 1970-01-01
      • 2015-05-04
      相关资源
      最近更新 更多