【问题标题】:How to retrieve/provide a CSRF token to/from Django as an API如何作为 API 向/从 Django 检索/提供 CSRF 令牌
【发布时间】:2015-10-25 21:12:15
【问题描述】:

我正在开发一个项目,该项目使用 Django REST 框架作为后端(假设在 api.somecompany.com,但有一个 React.js 前端(在 www.somecompany.com)不由 Django 提供,它发出 AJAX 请求。

因此,我不能使用 Django 让模板包含 CSRF 令牌的传统方法,例如 <form action="." method="post">{% csrf_token %}

我可以向 Django REST Framework 的 api-auth\login\ url 发出请求,它将返回此标头: Set-Cookie:csrftoken=tjQfRZXWW4GtnWfe5fhTYor7uWnAYqhz; expires=Mon, 01-Aug-2016 16:32:10 GMT; Max-Age=31449600; Path=/ - 但我无法检索此 cookie 以使用 X-CSRFToken 发送回我的 AJAX 请求(我的理解是单独的子域),并且它似乎没有自动包含在内。

这是我的相关代码:

// 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));
}
$.ajaxSetup({
    beforeSend: function(xhr, settings) {
        if (!csrfSafeMethod(settings.type)) {
            xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
        }
    }
});

当页面加载时,我调用它以确保我有一个令牌:

$.ajax(loginUrl, { method: "OPTIONS", async: false })
    .done(function(data, textStatus, jqXHR) {
        console.log(jqXHR)
        app.csrftoken@ = $.cookie("csrftoken")
        console.log($.cookie("csrftoken"))
        console.log(app.csrftoken)
    })
    .fail(function(jqXHR, textStatus, errorThrown) {
        console.log(jqXHR)
    });

这并不完全干净,但我还没有向自己证明这个概念。

当前端和后端位于不同的端口/域时,验证/防止 CSRF 的“正确”方法是什么?

【问题讨论】:

    标签: jquery ajax django reactjs django-csrf


    【解决方案1】:

    事实上,您可以使用允许的请求来源的白名单,通过 CSRF 保护来实现这一点。

    要完成这项工作,您将不得不使用Cross-Origin Resource Sharing (CORS)。为此,我建议创建一个中间件类来设置您的交叉共享凭据。有一个 great gist 概述了如何使用跨源 HTTP 请求标头中的一些构建。如果需要,您可以通过这种方式通过中间件插入 CSRF 令牌,方法是更改​​请求的源引用值。

    django-cors-headers 应用程序会为您执行此操作。如果您有兴趣,可以在their middleware file 中查看他们是如何协商 CSRF 令牌的。

    请参阅Django REST CORS Docs 了解更多信息(他们建议使用 django-cors-headers)。

    如果您仍然遇到困难,请尝试:

    1. 将 AJAX 请求的 crossDomain 参数设置为 True。有时,如果没有指定,jQuery 不会处理请求。
    2. 如果您仍然没有在请求中收到令牌标头,请尝试在您的视图方法周围附加ensure_csrf_cookie() 装饰器。有时,当页面上没有{% csrf_token %} 模板标签时(如果您不呈现表单),Django 根本不会在请求中包含令牌。

    【讨论】:

    • 我想我已经涵盖了 - 我的请求并没有因为 CORS 原因而失败。据我所知,我的问题是我的第一个请求(OPTIONS 一个)没有设置csrftoken cookie,所以我无法读取它以添加为标题,或者让它自动发送后续要求。直接发出请求(不使用 AJAX)正确设置 cookie。有什么东西阻止设置 cookie 吗?
    • 如果是这种情况,我可以想到另外两件事可能会出错:首先,确保将 AJAX 请求的 crossDomain 参数设置为 True,如 api 中所述.jquery.com/jQuery.ajax。也有可能,如果您没有在要呈现的页面上包含 CSRF 模板标签(即页面没有表单),则 Django 默认可能不会在标头中包含令牌。您可以在视图周围使用 ensure_csrf_cookie(view) 装饰器来强制执行此操作(请参阅 docs.djangoproject.com/en/dev/ref/csrf/…)
    猜你喜欢
    • 2020-10-29
    • 1970-01-01
    • 2015-12-19
    • 2021-03-15
    • 2017-01-09
    • 2014-08-31
    • 2015-06-09
    • 2020-08-07
    • 2013-02-09
    相关资源
    最近更新 更多