15

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

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

4

7 回答 7

13

不幸的是,目前 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):
    #...

此外,您可能想查看“自动装饰 django 项目的所有视图”博客文章

于 2010-02-12T21:21:26.990 回答
7

正如之前的海报所提到的,不需要登录是默认设置。

但是,有时阻止登录用户的某些视图很有用——例如,登录用户能够使用站点的注册页面是没有意义的。在这种情况下,您可以根据现有的 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
于 2011-08-15T22:03:14.523 回答
2

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

from django.contrib.auth.decorators import user_passes_test

@user_passes_test(lambda u: u.is_anonymous)
于 2017-05-05T07:25:44.437 回答
1

您可以使用 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
于 2021-06-15T07:35:26.557 回答
0

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

于 2010-02-12T18:57:27.060 回答
0

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

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

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

为了解决这个问题,我编写了自己的login_requiredand login_not_required. Mylogin_required基于 django's django.contrib.auth.decorators.login_required,但稍作修改以实际关心何时将视图标记为不需要登录。

我的项目名为mysite.

我的应用名为my_secret_app.

我的公众视野my_secret_app被称为MyPublicView

我的整个解决方案如下所示:

我的站点/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

我的网站/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):
        ...

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

@login_not_required
def MyPublicView(request):
    ...
于 2020-01-20T18:20:52.513 回答
-1

@permission_classes([permissions.AllowAny])

于 2017-12-28T23:20:57.837 回答