记一次由Django的CSRF引发的“血案”
在从前台提交表单的时候,服务端报出CSRF_TOKEN的错误,这不合理啊,老夫明明在前端的页面已经加上了,可为什么还会报错呢。
经过我一番的查阅文档和博客,终于找到问题所在了。
其实问题并没有那么复杂,却暴露了我对Django方法的不求甚解。
事情起源于,一篇博客。我在网上找前端显示用户登陆名称的时候,在网上找到一篇博客。上面对session写的浅显易懂,我就照着上面的代码敲了一遍,也运行起来了,也解决了我对于session的操作的需求。
可咱是谁啊,肯定不能就此满足啊。仔细的看了看代码,发现CSRF的中间件给注释了,那怎么行啊,多危险万一被坏人给利用,不就GG了,然后我就给解掉注释了。
故事正式开始:
第一章 CSRF错误
代码运行,然后就报了第一个错误:
看到这个错误我想了一哈,好像...渲染的模版没有添加
{% csrf_token %}
,那就加上吧(在这里其实还有一个小的插曲,我没用过Django自带的Form表单类,不知道这种类型的怎么添加啊,心想那就上官网查一下吧,当看到官方给出的示例跟我设想的一样,非常开心,果然英雄所见略同啊 )kzX91O.png
第二章 还是CSRF错误
虽然加上了,但还是报了报了第二个错:
kzOd0A.png
咦,为啥感觉跟之前的错误好相似啊,其实根本不用感觉,因为它根本就是同一个错误,但是我明明已经按照官网上的说明加了啊,而且前端也已经有这个CSRF的COOKIE,可为啥还报错啊。
kzXxbj.png
然后我就仔细的看了下Django后台报出的错误,这下可明白了,原来是渲染模版的时候有这个值了,但是在上下文中却没有提供这个值。
UserWarning: A {% csrf_token %} was used in a template, but the context did not provide the value.
This is usually caused by not using RequestContext.
然后我又陷入了沉思中,又仔细的看了一遍照着博客敲的代码,发现有好几个模块中的方法似曾相识,但是却没用过,然后根据后台的报错信息和使用的陌生而又熟悉的方法,我又陷入了疯狂查找资料的状态下。功夫不负有心人,我又找到了相关的信息,Django中的render
方法还是跟render_to_response
方法还是有 区别的[1]
哦哈哈哈,终于在老夫的淫威下屈服了,真是得劲。
第三章 request对象未响应给前端,导致模版报错
那赶紧改过来吧,我就赶紧把render方法进行导入,然后把render_to_response替换掉。重启服务,重新访问。
嘤嘤嘤,报错了,吐血。。。看来老夫还是道行太浅啊
kzvRne.png
这是怎么回事,我明明返回了一个页面啊,可是它竟然告诉我说,login路径下的模版不存在,Excuse ME,你怕不是在逗我吧,我眼睛虽然近视,可是矫正后不也是5.0吗,可是后台报错中也没有太多的有用信息啊。
django.template.exceptions.TemplateDoesNotExist:
{'uf': <UserForm bound=False, valid=Unknown, fields=(username;password)>}
那怎么搞啊,既然我给了,而它却说没收着。嗯,这其中必有问题,那我看看render的源码吧。好嘛,这一看不要紧,看的我真是老脸一红啊。WTF,弄了半天,我竟然忘传request参数了,真是阴沟里翻了船啊。
kzxFuF.png
那咱赶紧改上吧,这次可不能先得意了,笑不到最后那可是最大的笑话。
哈哈哈,这下问题终于解决了,终于没有错误了。
后记:
虽然事情的起因是,我需要使用session来获取某些内容,而参照网上的博客。
由于博客年代久远,使用的某些方法现在都已不常用了,又加上我学习的不甚扎实,才导致后续的一系列的报错。虽然错误一跟错误二看起来是一样的,但实质上还是有所区别的,无他输入的条件是不一样的,解决不了前面的错误,后面的错误还是暴露不出来。
涉及到的知识点:
- Django的 Form 类
- CSRF在Form类中的使用
- render与render_to_response的区别
- CSRF_TOKEN的使用
第四章 报错信息重中之重
其实说了这么多废话,最重要的信息还是在第一个报错信息中,人家说的很明确,要想POST表单不会报这个错,你必须得确保:
- 你的浏览器必须得能接收COOKIE。
- 视图函数是通过模版中的render方法提交request参数(HttpRequest对象)的。
- 在模版中,每个POST表单中都必须有一个{%csrf_token%}模板标签,用于定位内部URL地址。
- 如果不使用CsrfViewMiddleware中间件,则必须在使用csrf_token模板标签的任何视图以及接受POST数据的视图上使用csrf_protect。
- 表单必须得有有效的CSRF令牌。在登录后登录其他浏览器选项卡或点击后退按钮后,可能需要使用表单重新加载页面,因为登录后会重新生成CSRF_TOKEN。
哈哈,报错信息很重要吧,如果一开始就仔细看的话,后面的很多问题就能避免了。
参考文档:
Django的 Form 类(官方文档)
django 使用session
Django提交表单时遇到403错误:CSRF verification failed
Django中render和render_to_response的区别
-
render()方法是Django1.3后新出的,它是render_to_response()方法更加简单快捷的实现,前者会自动使用RequestContext(但是必须得进行传参),而后者必须得写出来。 ↩