具有隐藏所有网站页面的登录名和密码的 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 应用程序界面的页面。而且,当然,我希望中间件应该隐藏应用程序的所有页面。
再次:
- 当我将代码放入 process_request 时,我可以接听任何电话
到网站上的任何页面,但不呈现 csrf。结果当用户提交表单时出现 csrf 错误。
- 当我放置
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推荐的方法。
我编写了以下中间件,它呈现表单并要求用户输入登录名和密码。中间件应该应用于整个网站:
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 应用程序界面的页面。而且,当然,我希望中间件应该隐藏应用程序的所有页面。
再次:
- 当我将代码放入 process_request 时,我可以接听任何电话 到网站上的任何页面,但不呈现 csrf。结果当用户提交表单时出现 csrf 错误。
- 当我放置 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推荐的方法。