使Django的login_required成为默认值的最佳方法

sam*_*gar 96 python django

我正在开发一个大型Django应用程序,其中绝大多数需要登录才能访问.这意味着我们在整个应用程序中都洒了:

@login_required
def view(...):
Run Code Online (Sandbox Code Playgroud)

这很好,只要我们记得在任何地方添加它,它就会很好!可悲的是,有时我们会忘记,而失败往往不是非常明显.如果一个视图的唯一链接在@login_required页面上,那么你不可能注意到你可以在没有登录的情况下实际到达该视图.但坏人可能会注意到,这是一个问题.

我的想法是扭转系统.而不是必须在任何地方键入@login_required,而是我有类似的东西:

@public
def public_view(...):
Run Code Online (Sandbox Code Playgroud)

只是为了公众的东西.我试图用一些中间件来实现它,我似乎无法让它工作.我想,我尝试的所有内容都与我们正在使用的其他中间件进行了很好的交互.接下来,我尝试编写一些东西来遍历URL模式,以检查那些不是@public的东西是否被标记为@login_required - 至少如果我们忘记了某些内容,我们会得到一个快速错误.但后来我无法弄清楚如何判断@login_required是否已应用于视图......

那么,正确的方法是什么?谢谢您的帮助!

Dan*_*aab 93

中间件可能是您最好的选择.我过去使用过这段代码,修改自其他地方的代码段:

import re

from django.conf import settings
from django.contrib.auth.decorators import login_required


class RequireLoginMiddleware(object):
    """
    Middleware component that wraps the login_required decorator around
    matching URL patterns. To use, add the class to MIDDLEWARE_CLASSES and
    define LOGIN_REQUIRED_URLS and LOGIN_REQUIRED_URLS_EXCEPTIONS in your
    settings.py. For example:
    ------
    LOGIN_REQUIRED_URLS = (
        r'/topsecret/(.*)$',
    )
    LOGIN_REQUIRED_URLS_EXCEPTIONS = (
        r'/topsecret/login(.*)$',
        r'/topsecret/logout(.*)$',
    )
    ------
    LOGIN_REQUIRED_URLS is where you define URL patterns; each pattern must
    be a valid regex.

    LOGIN_REQUIRED_URLS_EXCEPTIONS is, conversely, where you explicitly
    define any exceptions (like login and logout URLs).
    """
    def __init__(self):
        self.required = tuple(re.compile(url) for url in settings.LOGIN_REQUIRED_URLS)
        self.exceptions = tuple(re.compile(url) for url in settings.LOGIN_REQUIRED_URLS_EXCEPTIONS)

    def process_view(self, request, view_func, view_args, view_kwargs):
        # No need to process URLs if user already logged in
        if request.user.is_authenticated():
            return None

        # An exception match should immediately return None
        for url in self.exceptions:
            if url.match(request.path):
                return None

        # Requests matching a restricted URL pattern are returned
        # wrapped with the login_required decorator
        for url in self.required:
            if url.match(request.path):
                return login_required(view_func)(request, *view_args, **view_kwargs)

        # Explicitly return None for all non-matching requests
        return None
Run Code Online (Sandbox Code Playgroud)

然后在settings.py中,列出要保护的基本URL:

LOGIN_REQUIRED_URLS = (
    r'/private_stuff/(.*)$',
    r'/login_required/(.*)$',
)
Run Code Online (Sandbox Code Playgroud)

只要您的站点遵循需要身份验证的页面的URL约定,此模型就可以使用.如果这不是一对一的合适,您可以选择修改中间件以更贴合您的情况.

我喜欢这种方法 - 除了删除使用@login_required装饰器乱丢代码库的必要性- 如果认证方案发生变化,你有一个地方可以进行全局变更.

  • 应该将中间件RequireLoginMiddleware类放在哪里?views.py,models.py? (4认同)

Ber*_*Ber 29

可以在每个视图函数上放置装饰器.您也可以将login_required()装饰器放在urls.py文件中.虽然这仍然是一项手动任务,但至少您可以在一个地方完成所有操作,这使审计变得更容易.

例如,

    from my_views import home_view

    urlpatterns = patterns('',
        # "Home":
        (r'^$', login_required(home_view), dict(template_name='my_site/home.html', items_per_page=20)),
    )

请注意,视图函数是直接命名和导入的,而不是字符串.

另请注意,这适用于任何可调用的视图对象,包括类.


and*_*ndy 5

在 Django 2.1 中,我们可以用以下方法修饰类中的所有方法

from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
from django.views.generic import TemplateView

@method_decorator(login_required, name='dispatch')
class ProtectedView(TemplateView):
    template_name = 'secret.html'
Run Code Online (Sandbox Code Playgroud)

更新: 我还发现以下方法有效:

from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import TemplateView

class ProtectedView(LoginRequiredMixin, TemplateView):
    template_name = 'secret.html'
Run Code Online (Sandbox Code Playgroud)

LOGIN_URL = '/accounts/login/'并在你的settings.py中设置