如何作为 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 请求?
Django - 使用从类似 REST 的 API 检索的数据构建报告的应用程序