通过我的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))

谢谢你的回复!


当前回答

更新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.

其他回答

One CSRF token is assigned to every session ( i.e. every time you log in). So before you wish to get some data entered by user and send that as ajax call to some function which is protected by csrf_protect decorator, try to find the functions that are being called before you are getting this data from user. E.g. some template must be being rendered on which your user is entering data. That template is being rendered by some function. In this function you can get csrf token as follows: csrf = request.COOKIES['csrftoken'] Now pass this csrf value in context dictionary against which template in question is being rendered. Now in that template write this line: Now in your javascript function, before making ajax request, write this: var csrf = $('#csrf').val() this will pick value of token passed to template and store it in variable csrf. Now while making ajax call, in your post data, pass this value as well : "csrfmiddlewaretoken": csrf

这将工作,即使你没有实现django表单。

实际上,这里的逻辑是:您需要从请求中获得的令牌。 因此,您只需要在登录后立即找出被调用的函数。一旦你有了这个令牌,要么进行另一个ajax调用来获取它,要么将它传递给一些可以通过ajax访问的模板。

我有一个解决办法。在我的JS我有两个函数。 首先得到饼干(即。csrftoken):

function getCookie(name) {
let cookieValue = null;
if (document.cookie && document.cookie !== '') {
    const cookies = document.cookie.split(';');
    for (let i = 0; i < cookies.length; i++) {
        const cookie = cookies[i].trim();
        // Does this cookie string begin with the name we want?
        if (cookie.substring(0, name.length + 1) === (name + '=')) {
            cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
            break;
        }
    }
}
return cookieValue;

}

第二个是我的ajax函数。在这种情况下,它是用于登录的,实际上不返回任何东西,只是传递值来设置一个会话:

function LoginAjax() {


    //get scrftoken:
    const csrftoken = getCookie('csrftoken');

    var req = new XMLHttpRequest();
    var userName = document.getElementById("Login-Username");
    var password = document.getElementById("Login-Password");

    req.onreadystatechange = function () {
        if (this.readyState == 4 && this.status == 200) {            
            //read response loggedIn JSON show me if user logged in or not
            var respond = JSON.parse(this.responseText);            
            alert(respond.loggedIn);

        }
    }

    req.open("POST", "login", true);

    //following header set scrftoken to resolve problem
    req.setRequestHeader('X-CSRFToken', csrftoken);

    req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
    req.send("UserName=" + userName.value + "&Password=" + password.value);
}

你可以把这个js粘贴到你的html文件中,记住把它放在其他js函数之前

<script>
  // using jQuery
  function getCookie(name) {
    var cookieValue = null;
    if (document.cookie && document.cookie != '') {
      var cookies = document.cookie.split(';');
      for (var i = 0; i < cookies.length; i++) {
        var cookie = jQuery.trim(cookies[i]);
        // Does this cookie string begin with the name we want?
        if (cookie.substring(0, name.length + 1) == (name + '=')) {
          cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
          break;
        }
      }
    }
    return cookieValue;
  }

  function csrfSafeMethod(method) {
    // these HTTP methods do not require CSRF protection
    return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
  }

  $(document).ready(function() {
    var csrftoken = getCookie('csrftoken');
    $.ajaxSetup({
      beforeSend: function(xhr, settings) {
        if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
          xhr.setRequestHeader("X-CSRFToken", csrftoken);
        }
      }
    });
  });
</script>

使用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 %}

I've just encountered a bit different but similar situation. Not 100% sure if it'd be a resolution to your case, but I resolved the issue for Django 1.3 by setting a POST parameter 'csrfmiddlewaretoken' with the proper cookie value string which is usually returned within the form of your home HTML by Django's template system with '{% csrf_token %}' tag. I did not try on the older Django, just happened and resolved on Django1.3. My problem was that the first request submitted via Ajax from a form was successfully done but the second attempt from the exact same from failed, resulted in 403 state even though the header 'X-CSRFToken' is correctly placed with the CSRF token value as well as in the case of the first attempt. Hope this helps.

问候,

Hiro