持久的“CSRF令牌缺失或不正确”。Jinja与django-registration设置
我收到了这个消息:
CSRF令牌缺失或不正确。
在大多数论坛上,他们告诉你要在表单中加入 {% csrf_token %},而我确实有这个。
另外,我在我的settings.py文件中也有相关设置:
TEMPLATE_CONTEXT_PROCESSORS = (
"django.contrib.auth.context_processors.csrf",
"django.contrib.auth.context_processors.auth",
)
我在使用jinja模板,这个似乎不需要CSRF令牌,但后来我安装了django注册功能,就搞不清楚了,因为它似乎使用了一些我没有接触过的视图,也就是说,这些视图不是我写的,我也找不到它们在哪里。人们称这些为“标准认证视图”。所以我无法添加“RequestContext”。
有没有人知道发生了什么,以及我该如何解决这个问题?谢谢!
5 个回答
这个回答不是专门针对django-registration的,而是关于如何在使用Django和Jinja2时的一些通用问题。
Django的CsrfViewMiddleware会设置一个叫csrf_token的cookie,前提是它发现你访问了csrf_token这个上下文成员。可惜的是,Jinja2的渲染是在Django的中间件执行之后才进行的。因此,这个cookie没有被设置好,导致它和表单不匹配,结果你就会遇到403错误。
要解决这个问题,你需要在处理响应的过程中,某个时刻访问context['csrf_token']。
如果你使用的是基于类的视图,你可以创建一个叫CsrfProtectMixin的混入类:
class CsrfProtectMixin(object):
def render_to_response(self, context, **response_kwargs):
# Csrf processing happens when using a RequestContext.
# Be sure to use one.
if not isinstance(context, RequestContext):
context = RequestContext(self.request, context)
# Force access to csrf_token due to the way jinja2 renders the template
# after middleware has finished processing. Otherwise, the csrf cookie
# will not be set.
str(context.get('csrf_token'))
return super(CsrfProtectMixin, self).render_to_response(context, **response_kwargs)
然后在你的视图类中使用它:
class MyView(CsrfProtectMixin, TemplateView):
def get(self, request, *args, **kwargs):
context = {}
return self.render_to_response(context)
如果你没有使用基于类的视图,你可以尝试这样做:
def my_view(request):
context = RequestContext(request)
str(context['csrf_token']) #force access to the csrf_token
return render_to_response('template.html', context)
或者你也可以考虑用上面类中的逻辑来修改render_to_response。
你有没有安装标准的Django模板系统?大多数带有模板的应用都需要这个。
关于CSRF(跨站请求伪造),一个叫做上下文处理器的东西会把一个名为'csrf_token'的变量放到响应的上下文中,这个变量是从中间件中获取的(如果启用了的话)。现在你只需要确保这个变量包含在你的表单里。
这部分内容直接来自django.core,随时可能会有变化。
if csrf_token:
if csrf_token == 'NOTPROVIDED':
return mark_safe(u"")
else:
return mark_safe(u"<div style='display:none'><input type='hidden' name='csrfmiddlewaretoken' value='%s' /></div>" % csrf_token)
不过,你只需要知道的是,在你的表单中必须有一个名为csrfmiddlewaretoken的输入类型,并且它的值要是context.get('csrf_token',''),就这样简单。
你可能需要手动重写django-registration的视图。看起来有一个问题,是关于Jinja和Django在配置模板加载器时的做法不太一致。
想查看标准的认证视图,可以在你的Python安装目录下的“site-packages”文件夹里找。
你可以尝试像这样包装标准的认证视图:
from django.contrib.auth.views import login, logout
from django.views.decorators.csrf import csrf_protect
@csrf_protect
def my_wrapped_login_view(request):
return login(request)
@csrf_protect
def my_wrapped_logout_view(request):
return logout(request)
我基本上是导入了Django的标准认证视图,并用我自己的视图调用它们,我的视图加上了csrf_protect装饰器。试试看吧。