如何作为 API 向/从 Django 检索/提供 CSRF 令牌

Posted

技术标签:

【中文标题】如何作为 API 向/从 Django 检索/提供 CSRF 令牌【英文标题】:How to retrieve/provide a CSRF token to/from Django as an API 【发布时间】: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 的“正确”方法是什么?

【问题讨论】:

【参考方案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)。

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

    将 AJAX 请求的 crossDomain 参数设置为 True。有时,如果没有指定,jQuery 不会处理请求。 如果您仍然没有在请求中收到令牌标头,请尝试在您的视图方法周围附加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/…)

以上是关于如何作为 API 向/从 Django 检索/提供 CSRF 令牌的主要内容,如果未能解决你的问题,请参考以下文章

如何将从表单提交中检索到的参数作为参数传递给 django 中的函数

如何从 ReactJS Web 应用程序向 Django API 发送 POST 请求?

快速向 API 发送和检索数据

Django - 使用从类似 REST 的 API 检索的数据构建报告的应用程序

无法从 reactjs 中的 axios 向 id=1 文章的 django REST API 后端发出 GET 请求

如何从 Django 发送异步 HTTP 请求并在 python2.7 中等待结果?