与Django视图的@login_required装饰器相反的是什么?

时间:2010-02-12 18:51:23

标签: django django-authentication

如果我想确保一个视图被列为具有公共访问权限,是否有一个与@public_access等效的装饰器,它与@login_required相反,并明确表示该视图应该是公共可访问的?

我想到的一个用例是自动将“@csrf_exempt”添加到所有公共视图中,此外还要在代码中明确说明该视图应该是公共可访问的。

7 个答案:

答案 0 :(得分:11)

不幸的是,Django目前没有内置支持,当@login_required被意外遗忘时,您可能会面临泄露敏感信息的风险。

以下是我的一个项目的解决方案:

middleware/security.py

def public(function):
    """Decorator for public views that do not require authentication
    """
    orig_func = function
    while isinstance(orig_func, partial):  # if partial - use original function for authorization
        orig_func = orig_func.func
    orig_func.is_public_view = True

    return function

def is_public(function):
    try:                                    # cache is found
        return function.is_public_view
    except AttributeError:                  # cache is not found
        result = function.__module__.startswith('django.') and not function.__module__.startswith('django.views.generic') # Avoid modifying admin and other built-in views

        try:                                # try to recreate cache
            function.is_public_view = result
        except AttributeError:
            pass

        return result


class NonpublicMiddleware(object):

    def process_view_check_logged(self, request, view_func, view_args, view_kwargs):
        return

    def process_view(self, request, view_func, view_args, view_kwargs):
        while isinstance(view_func, partial):  # if partial - use original function for authorization
            view_func = view_func.func

        request.public = is_public(view_func)
        if not is_public(view_func):
            if request.user.is_authenticated():     # only extended checks are needed
                return self.process_view_check_logged(request, view_func, view_args, view_kwargs)

            return self.redirect_to_login(request.get_full_path())  # => login page

    def redirect_to_login(self, original_target, login_url=settings.LOGIN_URL):
        return HttpResponseRedirect("%s?%s=%s" % (login_url, REDIRECT_FIELD_NAME, urlquote(original_target)))

settings.py

MIDDLEWARE_CLASSES = (
    #...
    'middleware.security.NonpublicProfilefullMiddleware',
    #...
)

最后,查看代码:

from <projname>.middleware import publi

@public
def some_view(request):
    #...

# Login required is added automatically
def some_private_view(request):
    #...

此外,您可能需要查看"Automatically decorating all views of a django project"博文

答案 1 :(得分:5)

正如之前提到的海报,默认情况下不需要登录。

但是,有时阻止登录用户的某些视图很有用 - 例如,登录用户无法使用网站的注册页面。在这种情况下,您可以根据现有的login_required装饰器

执行类似的操作
from django.contrib.auth.decorators import user_passes_test
from django.conf import settings

LOGGED_IN_HOME = settings.LOGGED_IN_HOME

def login_forbidden(function=None, redirect_field_name=None, redirect_to=LOGGED_IN_HOME):
    """
    Decorator for views that checks that the user is NOT logged in, redirecting
    to the homepage if necessary.
    """
    actual_decorator = user_passes_test(
        lambda u: not u.is_authenticated(),
        login_url=redirect_to,
        redirect_field_name=redirect_field_name
    )
    if function:
        return actual_decorator(function)
    return actual_decorator

答案 2 :(得分:1)

有点晚了,但解决这个问题的另一个简单方法是依赖另一个装饰器并添加一个lambda函数:

from django.contrib.auth.decorators import user_passes_test

@user_passes_test(lambda u: u.is_anonymous)

答案 3 :(得分:1)

@permission_classes([permissions.AllowAny])

答案 4 :(得分:0)

“不需要登录”是默认设置。如果你想注释一个视图永远不应该被登录限制,那么你应该在docstring中这样做。

答案 5 :(得分:0)

我使用django-decorator-include使用login_required装饰器来保护整个应用,而不是一次保护一个视图。我的应用程序的主要urls.py如下所示:

path('my_secret_app/', decorator_include(login_required, ('my_secret_app.urls', 'my_secret_app'))),

这很好用,除非我的一个应用程序具有一个需要公开的视图。

为解决这个问题,我编写了自己的login_requiredlogin_not_required。我的login_required是基于django的django.contrib.auth.decorators.login_required进行的,但是经过了稍微修改,可以真正在意将视图标记为不需要登录时的情况。

我的项目称为mysite

我的应用名为my_secret_app

我在my_secret_app中的公开视野称为MyPublicView

我的整个解决方案如下:

mysite / lib.py

from django.contrib.auth import REDIRECT_FIELD_NAME
from django.contrib.auth.decorators import user_passes_test

# A copy of django.contrib.auth.decorators.login_required that looks for login_not_required attr
def login_required(function=None, redirect_field_name=REDIRECT_FIELD_NAME, login_url=None):
    actual_decorator = user_passes_test(
        lambda u: u.is_authenticated,
        login_url=login_url,
        redirect_field_name=redirect_field_name
    )

    if function:
        login_req = getattr(function, "login_required", True)

        if login_req:
            return actual_decorator(function)
        else:
            return function
    else:
        return actual_decorator

# Decorator to mark a view as not requiring login to access
def login_not_required(f):
    f.login_required = False
    return f

mysite / urls.py

from .lib import login_required
path('my_secret_app/', decorator_include(login_required, ('my_secret_app.urls', 'my_secret_app'))),

my_secret_app / views.py:

from django.utils.decorators import method_decorator
from mysite.lib import login_not_required

class MyPublicView(View):
    @method_decorator(login_not_required)
    def dispatch(self, request, *args, **kwargs):
        return super().dispatch(request, *args, **kwargs)

    def get(self, request):
        ...

    def post(self, request, *args, **kwargs):
        ...

无论您是子类ViewListViewCreateViewUpdateViewTemplateView还是其他子类,您都应该能够做同样的事情其他任何一个。如果您使用函数作为视图,它也应该起作用,尽管我没有尝试过:

@login_not_required
def MyPublicView(request):
    ...

答案 6 :(得分:0)

您可以使用 user_passes_test 并添加一个要求,anonymous_required。这与 login_required 的工作方式相反,您可以在注册和登录页面上使用 - 用户在登录后仍然看到登录页面有点烦人。 你会这样做:

from django.contrib.auth.decorators import user_passes_test

#Anonymous required 
def anonymous_required(function=None, redirect_url=None):

   if not redirect_url:
       redirect_url = settings.LOGIN_REDIRECT_URL

   actual_decorator = user_passes_test(
       lambda u: u.is_anonymous,
       login_url=redirect_url
   )

   if function:
       return actual_decorator(function)
   return actual_decorator


#Application of the Decorator

@anonymous_required
def register(request):
    #Your code goes here