通过我的AJAX帖子,我可以使用一些帮助来遵守Django的CSRF保护机制。我遵循了这里的说明:

http://docs.djangoproject.com/en/dev/ref/contrib/csrf/

我已经复制了他们在该页面上的AJAX示例代码:

http://docs.djangoproject.com/en/dev/ref/contrib/csrf/#ajax

我把一个警告打印getCookie('csrftoken')的内容之前的xhr。setRequestHeader调用,它确实被一些数据填充。我不确定如何验证令牌是正确的,但我被鼓励它正在寻找和发送一些东西。

但是Django仍然拒绝我的AJAX帖子。

这是我的JavaScript:

$.post("/memorize/", data, function (result) {
    if (result != "failure") {
        get_random_card();
    }
    else {
        alert("Failed to save card data.");
    }
});

下面是我从Django中看到的错误:

[23/Feb/2011 22:08:29] "POST / remember / HTTP/1.1" 403 2332

我肯定我遗漏了什么,也许很简单,但我不知道是什么。我在SO周围搜索了一下,看到了一些关于通过csrf_exempt装饰器关闭视图的CSRF检查的信息,但我发现那没什么吸引力。我已经尝试过了,它是有效的,但如果可能的话,我宁愿让我的POST以Django设计的方式工作。

为了以防有用,这里是我的视图正在做的事情的要点:

def myview(request):

    profile = request.user.profile

    if request.method == 'POST':
        """
        Process the post...
        """
        return HttpResponseRedirect('/memorize/')
    else: # request.method == 'GET'

        ajax = request.GET.has_key('ajax')

        """
        Some irrelevent code...
        """

        if ajax:
            response = HttpResponse()
            profile.get_stack_json(response)
            return response
        else:
            """
            Get data to send along with the content of the page.
            """

        return render_to_response('memorize/memorize.html',
                """ My data """
                context_instance=RequestContext(request))

谢谢你的回复!


当前回答

将这一行添加到jQuery代码中:

$.ajaxSetup({
  data: {csrfmiddlewaretoken: '{{ csrf_token }}' },
});

和完成。

其他回答

下面是Django提供的一个简单的解决方案:

<script type="text/javascript">
// using jQuery
var csrftoken = jQuery("[name=csrfmiddlewaretoken]").val();

function csrfSafeMethod(method) {
    // these HTTP methods do not require CSRF protection
    return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
// set csrf header
$.ajaxSetup({
    beforeSend: function(xhr, settings) {
        if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
            xhr.setRequestHeader("X-CSRFToken", csrftoken);
        }
    }
});

// Ajax call here
$.ajax({
    url:"{% url 'members:saveAccount' %}",
    data: fd,
    processData: false,
    contentType: false,
    type: 'POST',
    success: function(data) {
        alert(data);
        }
    });
</script>

来源:https://docs.djangoproject.com/en/1.11/ref/csrf/

公认的答案很可能是在转移注意力。Django 1.2.4和1.2.5之间的区别在于AJAX请求需要CSRF令牌。

我在Django 1.3上遇到了这个问题,这是由于CSRF cookie一开始就没有设置造成的。除非必须,否则Django不会设置cookie。因此,在Django 1.2.4上运行的纯ajax或重度ajax站点可能永远不会向客户端发送令牌,然后需要令牌的升级将导致403错误。

理想的解决方法是: http://docs.djangoproject.com/en/dev/ref/contrib/csrf/#page-uses-ajax-without-any-html-form 但是您必须等到1.4版本,除非这只是文档与代码同步

Edit

还要注意的是,Django文档在jQuery 1.5中指出了一个bug,所以确保你使用的是1.5.1或更高版本的Django建议代码: https://docs.djangoproject.com/en/dev/ref/csrf/#ajax

在我的情况下,问题是与nginx配置,我已经从主服务器复制到一个临时禁用https,在第二个过程中不需要。

我不得不注释掉配置中的这两行,以使它再次工作:

# uwsgi_param             UWSGI_SCHEME    https;
# uwsgi_pass_header       X_FORWARDED_PROTO;

更新2022

在CSRF攻击中,无辜的终端用户被攻击者欺骗,提交了一个他们不打算提交的web请求

选项1

from django.views.decorators.csrf import csrf_exempt
from django.http.response import JsonResponse


@csrf_exempt
def commentDeletePost(request):
    if request.is_ajax() and request.method == 'POST':
        try:
            comment = Comment.objects.get(pk=request.POST['pk'])
            if comment.author != request.user:
                return JsonResponse({'e': 'Forbidden'}, status=403) 
            comment.delete()
            return JsonResponse({}, status=200)
        execpt Comment.DoesNotExist:
            return JsonResponse({'e': 'Not Found'}, status=404)

选项2

<div id="csrf">
    {% csrf_token %}
</div>
<script type="text/javascript">
    window.crud = {
        commentDelete: function(
            pk, 
            success,
            error, 
        ){
            $.ajax({
                headers: {'X-CSRFToken': document.getElementById('csrf').querySelector('input').value},
                type: "POST", 
                url: "{% url 'comment-delete-post' %}",
                data: {
                    pk: pk,
                }, 
                success: success, 
                error: error,
            })
        }, 
    }
</script>

two options have its own advantage. First option will discard csrf token, which will not protecte your site from csrf attacks, but it will allow user to send more than one request with same Ajax function. the second option will restrict user to send one Ajax request only since csrf token can only be used once, but it is more secure. I personally prefer option 1, since Ajax functions such as like, star, unlike requires more than one Ajax call, and it is not a risky function to allow user call more than once.

使用Django轻松调用ajax

(26.10.2020) 在我看来,这比正确答案更清晰、更简单。

视图

@login_required
def some_view(request):
    """Returns a json response to an ajax call. (request.user is available in view)"""
    # Fetch the attributes from the request body
    data_attribute = request.GET.get('some_attribute')  # Make sure to use POST/GET correctly
    # DO SOMETHING...
    return JsonResponse(data={}, status=200)

urls . py

urlpatterns = [
    path('some-view-does-something/', views.some_view, name='doing-something'),
]

ajax调用

ajax调用非常简单,但对于大多数情况来说已经足够了。您可以获取一些值并将它们放入数据对象中,然后在上面描述的视图中,您可以通过它们的名称再次获取它们的值。

你可以在django的文档中找到csrftoken函数。基本上只需要复制它,并确保在ajax调用之前呈现它,以便定义csrftoken变量。

$.ajax({
    url: "{% url 'doing-something' %}",
    headers: {'X-CSRFToken': csrftoken},
    data: {'some_attribute': some_value},
    type: "GET",
    dataType: 'json',
    success: function (data) {
        if (data) {
            console.log(data);
            // call function to do something with data
            process_data_function(data);
        }
    }
});

使用ajax将HTML添加到当前页面

这可能有点离题,但我很少看到这个使用,这是一个伟大的方式来减少窗口重定位以及手动html字符串创建javascript。

这与上面的非常相似,但这一次我们从响应中呈现html,而不需要重新加载当前窗口。

如果您打算从作为ajax调用响应接收到的数据中呈现某种html,那么从视图返回HttpResponse而不是JsonResponse可能会更容易。这允许您轻松地创建html,然后可以插入到元素中。

视图

# The login required part is of course optional
@login_required
def create_some_html(request):
    """In this particular example we are filtering some model by a constraint sent in by 
    ajax and creating html to send back for those models who match the search"""
    # Fetch the attributes from the request body (sent in ajax data)
    search_input = request.GET.get('search_input')

    # Get some data that we want to render to the template
    if search_input:
        data = MyModel.objects.filter(name__contains=search_input) # Example
    else:
        data = []

    # Creating an html string using template and some data
    html_response = render_to_string('path/to/creation_template.html', context = {'models': data})

    return HttpResponse(html_response, status=200)

视图的html创建模板

creation_template.html

{% for model in models %}
   <li class="xyz">{{ model.name }}</li>
{% endfor %}

urls . py

urlpatterns = [
    path('get-html/', views.create_some_html, name='get-html'),
]

主模板和ajax调用

这就是我们要向其中添加数据的模板。在这个例子中,我们有一个搜索输入和一个将搜索输入值发送到视图的按钮。然后视图返回一个HttpResponse,显示与我们可以在元素中呈现的搜索匹配的数据。

{% extends 'base.html' %}
{% load static %}
{% block content %}
    <input id="search-input" placeholder="Type something..." value="">
    <button id="add-html-button" class="btn btn-primary">Add Html</button>
    <ul id="add-html-here">
        <!-- This is where we want to render new html -->
    </ul>
{% end block %}

{% block extra_js %}
    <script>
        // When button is pressed fetch inner html of ul
        $("#add-html-button").on('click', function (e){
            e.preventDefault();
            let search_input = $('#search-input').val();
            let target_element = $('#add-html-here');
            $.ajax({
                url: "{% url 'get-html' %}",
                headers: {'X-CSRFToken': csrftoken},
                data: {'search_input': search_input},
                type: "GET",
                dataType: 'html',
                success: function (data) {
                    if (data) {
                        /* You could also use json here to get multiple html to
                        render in different places */
                        console.log(data);
                        // Add the http response to element
                        target_element.html(data);
                    }
                }
            });
        })
    </script>
{% endblock %}