持久的“CSRF令牌缺失或不正确”。Jinja与django-registration设置

5 投票
5 回答
6818 浏览
提问于 2025-04-16 07:41

我收到了这个消息:

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 个回答

2

这个回答不是专门针对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。

2

你有没有安装标准的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',''),就这样简单。

3

你可能需要手动重写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装饰器。试试看吧。

撰写回答