Django: 如何将login_required修饰符应用于整个网站(不包括静态媒体)?

28

示例提供了一个应用级别视图的片段,但如果我的"urls.py"文件中有很多不同的(包括模板在内的非应用级别)条目,我该如何将login_required装饰器应用于它们中的每一个?

(r'^foo/(?P<slug>[-\w]+)/$', 'bugs.views.bug_detail'),
(r'^$', 'django.views.generic.simple.direct_to_template', {'template':'homepage.html'}),
11个回答

29

我把这个代码放到了我的项目根目录下的 middleware.py 文件中(源自 http://onecreativeblog.com/post/59051248/django-login-required-middleware

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)

然后在我的settings.py文件中的MIDDLEWARE_CLASSES中添加了projectname.middleware.LoginRequiredMiddleware


7
我认为这个解决方案真的很棒。我稍微进行了调整,以支持“redirect”,并通过“reverse”获取登录URL。 - Tomas Tomecek
我认为作者还应该引用http://onecreativeblog.com/post/59051248/django-login-required-middleware。 - Eiyrioü von Kauyf
1
如果LOGIN_URL='/',那么这段代码片段顶部的斜杠被去除后,得到的空字符串将匹配任何URL,因此无法正常工作! - user3182532
1
@user3182532 你说得完全正确;尝试解析URL最好使用经过测试和证明的库,Django提供了这样的库;你提到缺少斜杠会破坏代码的例子非常恰当。鉴于这个答案已经有将近10年的历史了,我不会给它点踩,但是我已经发布了一个改进版本,使用路由而不是原来的方法,并且更加DRY。 - dKen
现在有一个包可以解决这个问题:django-login-required-middleware - jenniwren

14

对于那些较晚了解此内容的人,您可能会发现django-stronghold非常适合您的用例。您可以将任何想要公开的URL列入白名单,而其他URL则需要登录才能访问。

https://github.com/mgrouchy/django-stronghold


2
这个解决方案似乎比安装和维护自己的中间件更加优雅。运行得非常出色。 - shacker
我创建了一个 middleware.py 文件,其中包含 from django.utils.deprecation import MiddlewareMixinfrom stronghold.middleware import LoginRequiredMiddlewareclass LoginRequiredStrongholdMiddleware(MiddlewareMixin, LoginRequiredMiddleware): pass。接下来,我在设置文件中添加了这个自定义的中间件类,而不是默认的 stronghold 类。这在 Django 1.10 中运行良好。 - MiniGunnR

10

这是一个稍微简短的中间件。

from django.contrib.auth.decorators import login_required

class LoginRequiredMiddleware(object):
    def process_view(self, request, view_func, view_args, view_kwargs):
        if not getattr(view_func, 'login_required', True):
            return None
        return login_required(view_func)(request, *view_args, **view_kwargs)
您需要在不需要登录即可查看的每个视图中将 "login_required" 设置为False:
函数视图:
def someview(request, *args, **kwargs):
    # body of view
someview.login_required = False

基于类的视图:

class SomeView(View):
    login_required = False
    # body of view

#or

class SomeView(View):
    # body of view
someview = SomeView.as_view()
someview.login_required = False
这意味着您需要处理登录视图,但我总是编写自己的身份验证后端。

9
一些之前的回答已经过时(针对较旧版本的Django),或者引入了不良的编程实践(硬编码URL,不使用路由)。以下是我更加DRY、可持续/可维护的方法(改编自Mehmet's answer above)。
为了突出这些改进,这依赖于给URLs设置路由名称(这比使用硬编码的URLs / URIs更可靠,因为这些会发生变化并且具有尾随/前导斜杠)。
from django.utils.deprecation import MiddlewareMixin
from django.urls import resolve, reverse
from django.http import HttpResponseRedirect
from my_project import settings

class LoginRequiredMiddleware(MiddlewareMixin):
    """
    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 by setting a tuple of routes to ignore
    """
    def process_request(self, request):
        assert hasattr(request, 'user'), """
        The Login Required middleware needs to be after AuthenticationMiddleware.
        Also make sure to include the template context_processor:
        'django.contrib.auth.context_processors.auth'."""

        if not request.user.is_authenticated:
            current_route_name = resolve(request.path_info).url_name

            if not current_route_name in settings.AUTH_EXEMPT_ROUTES:
                return HttpResponseRedirect(reverse(settings.AUTH_LOGIN_ROUTE))

settings.py文件中,您可以定义以下内容:
AUTH_EXEMPT_ROUTES = ('register', 'login', 'forgot-password')
AUTH_LOGIN_ROUTE = 'register'

我喜欢你的想法。你的导入有一个缺陷。当使用选项 --settings=mysite.path.to.different.setting_file 时,它会失败。请改用 from django.conf import settings 而不是 from my_project import settings - Chris

3

这是适用于Django 1.10+的经典LoginRequiredMiddleware

from django.utils.deprecation import MiddlewareMixin

class LoginRequiredMiddleware(MiddlewareMixin):
    """
    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).
    """
    def process_request(self, request):
        assert hasattr(request, 'user'), """
        The Login Required middleware needs to be after AuthenticationMiddleware.
        Also make sure to include the template context_processor:
        'django.contrib.auth.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)

值得注意的差异:
  • path.to.LoginRequiredMiddleware 应该在 settings.py 的 MIDDLEWARE 中包含,而不是 MIDDLEWARE_CLASSES
  • is_authenticated 是一个布尔值而不是一个方法。
  • 查看文档获取更多信息(尽管某些部分不太清晰)。

这依赖于硬编码URL(这是不可靠的;它们经常更改,并且有时带有尾部/前导斜杠)。最好使用路由并让Django处理URL匹配。我在我的答案中发布了更新 - dKen

2

我对使用自定义中间件不是很熟悉。你能指出一个处理这个的 Django 1.2 兼容片段吗? - meder omuraliev
我发现了这个代码片段:http://djangosnippets.org/snippets/1179/ 虽然它是在2008年发布的。有人能否浏览一下并看看它是否可用? - meder omuraliev

0

Django 登录验证中间件

将以下代码放入 middleware.py 文件中:

from django.http import HttpResponseRedirect
from django.conf import settings
from django.utils.deprecation import MiddlewareMixin
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(MiddlewareMixin):
    def process_request(self, request):
        assert hasattr(request, 'user')
        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)

而在 settings.py 中:

LOGIN_URL = '/app_name/login'

LOGIN_EXEMPT_URLS=(
    r'/app_name/login/',
)

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

像这样: 'app_name.middleware.LoginRequiredMiddleware'

0

除了 meder omuraliev 的答案,如果你想要排除像这样的 url(用正则表达式):

url(r'^my/url/(?P<pk>[0-9]+)/$', views.my_view, name='my_url')

将其添加到EXEMPT_URLS列表中,如下所示:

LOGIN_EXEMPT_URLS = [r'^my/url/([0-9]+)/$']

字符串开头必须有r'..'


0

感谢@Ehsan Ahmadi

在新版本的Django中,中间件应该像这样编写(我的Django版本=4.1.1):

middleware.py

from django.contrib.auth.decorators import login_required
from django.urls import resolve
from django.utils.deprecation import MiddlewareMixin

AUTH_EXEMPT_ROUTES = ('captcha-image', 'login', 'captcha')

class RejectAnonymousUsersMiddleware(MiddlewareMixin):

    def process_view(self, request, view_func, view_args, view_kwargs):
        current_route_name = resolve(request.path_info).url_name

        if  request.user.is_authenticated:
            return

        if current_route_name in AUTH_EXEMPT_ROUTES:
            return


        return login_required(view_func)(request, *view_args, **view_kwargs)

settings.py

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'myproject.middleware.RejectAnonymousUsersMiddleware',  

]

0
如果您有很多视图,并且不想触及任何一个,您可以使用中间件来解决此问题。尝试下面的代码:

import traceback
from django.contrib.auth.decorators import login_required


class RejectAnonymousUsersMiddleware(object):

    def process_view(self, request, view_func, view_args, view_kwargs):
        current_route_name = resolve(request.path_info).url_name

        if current_route_name in settings.AUTH_EXEMPT_ROUTES:
            return

        if  request.user.is_authenticated:
            return

        return login_required(view_func)(request, *view_args, **view_kwargs)

注意事项:

  • 您必须将此中间件添加到settings.py的中间件部分的最底部
  • 您应该将此变量放在settings.py中
    • AUTH_EXEMPT_ROUTES = ('register', 'login', 'forgot-password')

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接