如何在 Django 的 ajax POST 中返回新的 csrf 令牌?

Posted

技术标签:

【中文标题】如何在 Django 的 ajax POST 中返回新的 csrf 令牌?【英文标题】:How to return new csrf token at ajax POST in Django? 【发布时间】:2016-01-02 09:49:00 【问题描述】:

我有一个简单的 Like 按钮,与 @csrf_exempt 一起使用时效果很好:

模板

<p id="like_count">  topic.likes </p> 
<span data-type="topic" title="Like">       % csrf_token %
 <i class="fa fa-thumbs-up" id="liket" name="topic.id">  

阿贾克斯

$(function()
$('#liket').click(function()
      $.ajax(
               type: "POST",
               url: "/like/",
               data: 
               'topic_id': $(this).attr('name'), 
               'csrfmiddlewaretoken': 'csrf_token'
               ,
               success: tlikeSuccess,
               dataType: 'html'               
                );
    );

);
function tlikeSuccess(data, textStatus, jqXHR)

    $('#like_count').html(data);

和观点:

#@csrf_exempt 
def topic_like(request):

    args = 
    if request.method == 'POST':

        user = request.POST.get('user')
        lu= request.user #User.objects.get(username= user)
        topic_id = int(request.POST.get('topic_id'))

        try:
            liked_topic = Topic.objects.get(id = topic_id)
        except:
            liked_topic = None  

        if TopicLike.objects.filter(liker=request.user.id, topic=topic_id).exists():

            liked_topic.likes -=1
            liked_topic.save()
            TopicLike.objects.filter(topic=topic_id, liker=request.user.id).delete()

        else:            
            liked_topic.likes +=1
            liked_topic.save()
            newliker = TopicLike(topic=topic_id, liker=request.user.id)
            newliker.save()          


    #args.update(csrf(request))
    args['likes'] = str(liked_topic.likes)
    return render(request, 'ajax_like.html', args)  

但是我不喜欢这种忽略 CSRF 的解决方法,因为它可能容易受到攻击。 另一方面,我无法将新的 CSRF 令牌返回到模板,因此我感谢您将 CSRF 集成到此按钮中的提示。

【问题讨论】:

Django docs 建议在标题中而不是在帖子数据中提供 csrf 令牌。 @Alasdair 请给我一个完整的答案。我不知道如何应用文档的建议。 我认为文档很清楚。你尝试了什么?你认为缺少什么?你不明白哪一部分? 【参考方案1】:

Django 在其docs 中已定义实际设置 AJAX 请求的标头,同时在 jQuery 1.5.1 及更高版本中使用settings.crossDomain 保护 CSRF 令牌不被发送到其他域。

获取令牌:

// 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;

var csrftoken = getCookie('csrftoken');

在头部设置 CSRFToken:

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

$.ajaxSetup(
    beforeSend: function(xhr, settings) 
        if (!csrfSafeMethod(settings.type) && !this.crossDomain) 
            xhr.setRequestHeader("X-CSRFToken", csrftoken);
        
    
);

也可以试试这个方法在SO link.给出的每个AJAX请求中嵌入CSRF令牌

$(function () 
    $.ajaxSetup(
        headers:  "X-CSRFToken": getCookie("csrftoken") 
    );
);

【讨论】:

我将这些函数添加到我的 ajax 文件中,并在我看来删除了@csrf_exempt ,但我在领事Uncaught ReferenceError: csrftoken is not defined 中收到此错误。有什么想法吗? 对不起,我很困惑。你的意思是我应该将上面的所有 3 个函数添加到我的 ajax.js 中而不改变我原来的 ajax 函数和视图? 只是前 2 个方法,last(3rd) 函数是第 2 个函数的替代方法。 当我将上面的 2 个函数添加到我的 ajax.js 时,无论我是否在视图中更新 CSRF,我都会得到 403 (FORBIDDEN)。 (不是我没有对我原来的 ajax 函数做任何改变。我应该有吗?) 这两个函数在官方文档中定义了如何在请求中添加 csrftoken。如果您遵循了确切的程序,请检查docs。【参考方案2】:

您可以使用 javascript 手动添加 csrf 令牌。

https://docs.djangoproject.com/en/1.8/ref/csrf/#ajax

【讨论】:

您能否详细说明在我的特殊情况下如何手动生成 csrf?

以上是关于如何在 Django 的 ajax POST 中返回新的 csrf 令牌?的主要内容,如果未能解决你的问题,请参考以下文章

Django 如何让ajax的POST方法带上CSRF令牌

通过 Ajax 调用将数组发送到控制器,然后控制器在 Laravel 中返​​回具有该数组的另一个视图

如何在 Django 视图中使用 ajax POST 解析 json 数据

如何使用 Django 和 Jquery 实现 ajax?

如何在 Django 的 ajax POST 中返回新的 csrf 令牌?

如何使用带有 ajax 的 DJANGO REST 框架发出 POST 请求