让Django登录的最佳方法是需要defau

2024-09-24 22:27:45 发布

您现在位置:Python中文网/ 问答频道 /正文

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

@login_required
def view(...):

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

我的想法是扭转这种体制。我不必在任何地方都输入@login_required,取而代之的是:

@public
def public_view(...):

只是为了公共事务。我试着用一些中间件来实现它,但我似乎无法让它工作。我想,我尝试的所有东西都与我们正在使用的其他中间件交互得很糟糕。接下来,我尝试编写一些东西来遍历URL模式,以检查所有不属于@public的内容是否都标记为@login_required—至少这样,如果我们忘记了一些东西,我们会很快出错。但后来我不知道如何判断@login\u required是否已应用于视图。。。

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


Tags: 中间件djangoview视图应用程序链接def地方
3条回答

在Django中,如果不修改url的传递方式来查看函数,就很难更改内置的假设。

这里不是在Django内部胡闹,而是一个你可以使用的审计。只需检查每个视图功能。

import os
import re

def view_modules( root ):
    for path, dirs, files in os.walk( root ):
        for d in dirs[:]:
            if d.startswith("."):
                dirs.remove(d)
        for f in files:
            name, ext = os.path.splitext(f)
            if ext == ".py":
                if name == "views":
                    yield os.path.join( path, f )

def def_lines( root ):
    def_pat= re.compile( "\n(\S.*)\n+(^def\s+.*:$)", re.MULTILINE )
    for v in view_modules( root ):
        with open(v,"r") as source:
            text= source.read()
            for p in def_pat.findall( text ):
                yield p

def report( root ):
    for decorator, definition in def_lines( root ):
        print decorator, definition

运行此命令并在没有适当的修饰符的情况下检查defs的输出。

有一种方法可以替代在每个视图函数上放置decorator。您还可以将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)),
    )

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

还要注意,这适用于任何可调用的视图对象,包括类。

中间件可能是您的最佳选择。我以前用过这段代码,是从别处找到的代码片段中修改的:

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

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

LOGIN_REQUIRED_URLS = (
    r'/private_stuff/(.*)$',
    r'/login_required/(.*)$',
)

只要您的站点遵循需要身份验证的页面的URL约定,这个模型就可以工作。如果这不是一对一的适合,您可以选择修改中间件以更紧密地适应您的环境。

我喜欢这种方法-除了消除用@login_required装饰器乱扔代码基的必要性之外-是如果身份验证方案更改,您有一个地方可以进行全局更改。

相关问题 更多 >