如果我想确保一个视图被列为具有公共访问权限,是否有一个与 @public_access 等效的装饰器,它与 @login_required 相反,并明确表示该视图应该始终可以公开访问?
我想到的一个用例是自动将“@csrf_exempt”添加到所有公共视图中,此外还要在代码中明确表示视图应该可以公开访问。
如果我想确保一个视图被列为具有公共访问权限,是否有一个与 @public_access 等效的装饰器,它与 @login_required 相反,并明确表示该视图应该始终可以公开访问?
我想到的一个用例是自动将“@csrf_exempt”添加到所有公共视图中,此外还要在代码中明确表示视图应该可以公开访问。
不幸的是,目前 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 项目的所有视图”博客文章
正如之前的海报所提到的,不需要登录是默认设置。
但是,有时阻止登录用户的某些视图很有用——例如,登录用户能够使用站点的注册页面是没有意义的。在这种情况下,您可以根据现有的 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
有点晚了,但解决这个问题的另一种简单方法是依赖另一个装饰器并添加一个 lambda 函数:
from django.contrib.auth.decorators import user_passes_test
@user_passes_test(lambda u: u.is_anonymous)
您可以使用 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
“不需要登录”是默认设置。如果你想注释一个视图永远不应该被登录限制,那么你应该在文档字符串中这样做。
我使用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):
...
@permission_classes([permissions.AllowAny])