具有隐藏所有网站页面的登录名和密码的 Django 中间件

Django middleware with login and password which hides all the website pages

我编写了以下中间件,它呈现表单并要求用户输入登录名和密码。中间件应该应用于整个网站:

class InviteLoginForWebsiteMiddleware(object):

    def process_request(self, request):
        if request.session.get('has_invite') == True:
            return None

        form = WebsiteLoginForm()
        extra_context = dict()
        extra_context['form'] = form
        template_name = 'websiteLogin.html'

        if request.method == "POST":
            form = WebsiteLoginForm(request.POST)
            if form.is_valid():
                login = form.cleaned_data['login']
                password = form.cleaned_data['password']

                if login == "mylogin" and password == "mypassword":
                    request.session['has_inv'] = True
                    return None

        return ExtraContextTemplateView.as_view(template_name=template_name, extra_context=extra_context)(request)

此解决方案的问题是,当我在 process_request 中创建表单时,呈现的页面上缺少 csrf 令牌。我一直在寻找答案,发现开发人员建议在 process_view

中生成表单并进行处理

将所有代码移动到 process_view 后,如:

def process_view(self, request, view_func, view_args, view_kwargs):
    if request.session.get('has_inv') == True:
        return None

    form = WebsiteLoginForm()
    extra_context = dict()
    extra_context['form'] = form
    template_name = 'websiteLogin.html'

    if request.method == "POST":
        form = WebsiteLoginForm(request.POST)
        if form.is_valid():
            login = form.cleaned_data['login']
            password = form.cleaned_data['password']

            if login == "mylogin" and password == "mypassword":
                request.session['has_inv'] = True
                return None

    return ExtraContextTemplateView.as_view(template_name=template_name, extra_context=extra_context)(request)

代码开始运行,生成了 csrf 令牌,我可以提交带有登录名和密码的表单。

当用户输入无效页面时出现问题,例如 www.mysite。com/notworkingurl/。在这种情况下,process_view 不工作,引发 404 错误,用户被重定向到部分显示 Web 应用程序界面的页面。而且,当然,我希望中间件应该隐藏应用程序的所有页面。

再次:

  1. 当我将代码放入 process_request 时,我可以接听任何电话 到网站上的任何页面,但不呈现 csrf。结果当用户提交表单时出现 csrf 错误。
  2. 当我放置 process_view 中的代码一切正常 它存在的情况。如果它不存在,则根据以下重定向用户 404 和其他异常网址。而且我不希望发生这种重定向。

谁能提出解决这个问题的好方法?

解法:

感谢@knbk 的建议,一种可能的解决方案是使用 csrf_protect 装饰器。为了实现这个想法,我修改了我的观点 class 如下:

class ExtraContextTemplateViewCsrfProtect(TemplateView):
  extra_context = None

  @method_decorator(csrf_protect)
  def dispatch(self, request, *args, **kwargs):
    return super(ExtraContextTemplateViewCsrfProtect, self).dispatch(request, *args, **kwargs)

  def get_context_data(self, *args, **kwargs):
    context = super(ExtraContextTemplateViewCsrfProtect, self).get_context_data(*args, **kwargs)
    if self.extra_context:
      context.update(self.extra_context)
    return context

  post = TemplateView.get

也许你可以尝试使用需要登录的中间件:

Django Login Required Middleware

settings.py:

LOGIN_URL = '/login/'

LOGIN_EXEMPT_URLS = (
 r'^about\.html$',
 r'^legal/', # allow any URL under /legal/*
) 

MIDDLEWARE_CLASSES = (
    # ...
    'python.path.to.LoginRequiredMiddleware',
)                    

LoginRequiredMiddleware:

from django.http import HttpResponseRedirect
from django.conf import settings
from re import compile

EXEMPT_URLS = [compile(settings.LOGIN_URL.lstrip('/'))]
if hasattr(settings, 'LOGIN_EXEMPT_URLS'):
    EXEMPT_URLS += [compile(expr) for expr in settings.LOGIN_EXEMPT_URLS]

class LoginRequiredMiddleware:
    """
    Middleware that requires a user to be authenticated to view any page other
    than LOGIN_URL. Exemptions to this requirement can optionally be specified
    in settings via a list of regular expressions in LOGIN_EXEMPT_URLS (which
    you can copy from your urls.py).

    Requires authentication middleware and template context processors to be
    loaded. You'll get an error if they aren't.
    """
    def process_request(self, request):
        assert hasattr(request, 'user'), "The Login Required middleware\
 requires authentication middleware to be installed. Edit your\
 MIDDLEWARE_CLASSES setting to insert\
 'django.contrib.auth.middlware.AuthenticationMiddleware'. If that doesn't\
 work, ensure your TEMPLATE_CONTEXT_PROCESSORS setting includes\
 'django.core.context_processors.auth'."
        if not request.user.is_authenticated():
            path = request.path_info.lstrip('/')
            if not any(m.match(path) for m in EXEMPT_URLS):
                return HttpResponseRedirect(settings.LOGIN_URL)

您的中间件不仅缺少 csrf 令牌,它还容易受到 CSRF 攻击

如果您想使用此模式,而不是重定向到单个登录视图,您应该将所有表单逻辑移到实际视图中。然后,您可以使用 csrf_protect 来保护视图免受 CSRF 攻击,这也使您能够在模板中使用令牌:

class InviteLoginForWebsiteMiddleware(object):

    def process_request(self, request):
        if request.session.get('has_invite') == True:
            return None

        return csrf_protect(CustomLoginView.as_view())(request)

不过,我还是推荐matox推荐的方法。