如何在 Javascript 生成的 HTML 表单中包含 Django 1.2 的 CSRF 令牌?

Posted

技术标签:

【中文标题】如何在 Javascript 生成的 HTML 表单中包含 Django 1.2 的 CSRF 令牌?【英文标题】:How do I include Django 1.2's CSRF token in a Javascript-generated HTML form? 【发布时间】:2011-04-15 10:36:52 【问题描述】:

我最近升级到 Django 1.2.3,我的上传表单现在已经损坏。每当我尝试上传时,我都会收到“CSRF 验证失败。请求中止”。错误信息。

阅读 Django's documentation 关于这个主题后,它指出我需要在我的模板中的 html <form> 中添加 % csrf_token % 模板标签。不幸的是,我的<form> 是通过 javascript 生成的(特别是面板上 ExtJs 的“html”属性)。

长话短说,当我的 <form> 未包含在 Django 模板中时,如何将所需的 CSRF 令牌标签添加到我的 <form>

【问题讨论】:

【参考方案1】:

这将使用 csrf 令牌或在需要时自动生成一个新令牌。它将处理页面上的所有表单提交。如果您有非现场表单,则需要确保它们不会运行此代码。

<script>
$(document).on('submit', 'form[method=post]', function()
  if(!document.cookie.match('csrftoken=([a-zA-Z0-9]32)')) 
    for(var c = ''; c.length < 32;) c += 'abcdefghijklmnopqrstuvwxyz'.charAt(Math.random() * 26)
    document.cookie = 'csrftoken=' + c + '; path=/'
  
  if(!this.csrfmiddlewaretoken) $(this).append('<input type="hidden" name="csrfmiddlewaretoken">')
  $(this.csrfmiddlewaretoken).val(document.cookie.match('csrftoken=([a-zA-Z0-9]32)')[1])
)
</script>

需要 jQuery 1.7+

【讨论】:

【参考方案2】:

最简单的方法是使用 django 在您的页面上创建一个不做任何事情的隐藏表单。然后使用 JavaScript 获取表单,特别是表单中的令牌输入。最后,将该令牌输入插入或复制到您动态生成的表单中。

下面是两个示例,说明如何为 JavaScript 发布令牌。

<input id="csrf_token" value=" csrf_token "/>

<script type="text/javascript">
var CSRF_TOKEN = document.getElementById('csrf_token').value;
</script>

<script type="text/javascript">
var CSRF_TOKEN = " csrf_token ";
</script>

【讨论】:

@Huuuze:将csrf_token 放在模板中某处,并在表单构建期间使用JavaScript 对其进行解析。【参考方案3】:

这可能并不理想,但对我来说这是最快的解决方案。在“正文”底部的主模板中,我向我的库中添加了一个 javascript 函数。

<script type="text/javascript">
    MyToolkit.Utils.getCSRFToken = function () 
         return "% csrf_token %";
    ;
</script>

【讨论】:

该标签返回一个隐藏的 div。我想你的意思是 csrf_token 【参考方案4】:

更好的解决方案是从视图/模板生成js文件的代码。

然后,在视图中,您可以像这样在上下文中设置 csrf 令牌...

from django.core.context_processors import csrf
context = RequestContext(request)
context.update(csrf(request))

然后,在模板中,您可以使用 csrf_token 获取 csrf 令牌的原始值,然后使用它在表单中构建一个名为 csrfmiddlewaretoken 的隐藏字段。

【讨论】:

【参考方案5】:

另一种选择是使用 Ext 调整 the Django docs 中显示的基于 cookie/header 的解决方案 - 如果您有很多模板并且不想更改每一个模板,则更可取。

只需将以下 sn-p 放入您的 overrides.js(或您放置全局修改的任何位置):

Ext.Ajax.on('beforerequest', function (conn, options) 
   if (!(/^http:.*/.test(options.url) || /^https:.*/.test(options.url))) 
     if (typeof(options.headers) == "undefined") 
       options.headers = 'X-CSRFToken': Ext.util.Cookies.get('csrftoken');
      else 
       options.headers.extend('X-CSRFToken': Ext.util.Cookies.get('csrftoken'));
                             
   
, this);

(编辑:Ext已经有cookie读取功能,无需复制)

【讨论】:

我需要在动态生成的表单中做/添加什么吗? @john2x:不,您只需将代码放入您的 overrides.js 中。如果你不熟悉 overrides.js 的概念,下面的博客是一个好的开始:edspencer.net/2009/07/extoverride-monkey-patching-ext-js.html +1。太棒了,当我找到这个答案时,我也在思考同样的问题! 另外值得注意的是,如果您的模板不包含% csrf_token %csrftoken cookie 可能不存在。要强制始终设置 cookie,请使用导入为 from django.views.decorators.csrf import ensure_csrf_cookie@ensure_csrf_cookie 装饰您的视图。见docs.djangoproject.com/en/1.8/ref/csrf/#ajax【参考方案6】:

POSTing 的视图是否也回复GET?因为 JS 代码可以向相关视图发出 GET 请求并解析输出以提取 CSRF 令牌。我的 JS-fu 很弱,我不确定从客户端进行解析的效果如何。

有关广泛的相关示例,请参阅this question。在这种情况下,用户尝试使用 Python 脚本 POST 并由于同样的原因而失败。解决方案是相同的,只是他必须使用 Python 脚本而不是 JavaScript。

【讨论】:

以上是关于如何在 Javascript 生成的 HTML 表单中包含 Django 1.2 的 CSRF 令牌?的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript DOM 表操作

任何人都有一个很好的解决方案来抓取带有 Javascript 生成的内容(在本例中为 HTML 表)的页面的 HTML 源代码? [关闭]

如何在生成的 HTML 页面中隐藏 JavaScript 注释? [关闭]

如何防止用户生成的 HTML 中的 Javascript 注入攻击

如何使 PHP 或 JavaScript 生成 HTML 表单

如何使用 C# 执行 HTML 文件的所有 Javascript 以仅生成 HTML DOM