2010-02-12 15 views
11

Jeśli chcę mieć pewność, że widok jest oznaczony jako publiczny, czy istnieje dekorator równoważny @public_access, który byłby przeciwieństwem @login_required i wyraźnie pokazywał, że widok powinien być publicznie dostępny zawsze?Co jest przeciwieństwem @login_required decorator dla widoków Django?

Jedną z możliwych sytuacji, o której myślę, jest automatyczne dodanie "@csrf_exempt" do wszystkich publicznych widoków, a także wyjaśnienie w kodzie, że widok powinien być publicznie dostępny.

Odpowiedz

10

Niestety, obecnie nie ma wbudowanej obsługi tego w Django, co naraża na ryzyko ujawnienia poufnych informacji, gdy przypadkowo zapomnisz o @login_required.

Oto rozwiązanie od jednego z moich projektów:

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', 
    #... 
) 

i wreszcie, kod widok:

from <projname>.middleware import publi 

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

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

Możesz również spojrzeć na "Automatically decorating all views of a django project" wpis na blogu

+0

myślę, że jest również przydatna w niektórych przypadkach * * ograniczyć dostęp do niektórych funkcji, jeśli użytkownik jest zalogowany. na przykład, nie pozwalają zalogowany użytkownik, aby wypełnić formularz rejestracyjny ... W takim przypadku możesz użyć kombinacji of is_authenticated i is_anonymous: https://docs.djangoproject.com/en/dev/topics/auth/#authorization-for-anonymous-users – g33kz0r

0

"Login nie jest wymagany" to ustawienie domyślne. Jeśli chcesz dodać adnotację, że widok nigdy nie powinien być ograniczony do logowania, powinieneś to zrobić w docstringu.

5

Jak wspomniano w poprzednim plakacie, nie jest wymagane logowanie domyślne.

Czasami jednak przydatne jest blokowanie niektórych widoków od zalogowanych użytkowników - na przykład nie ma sensu, aby zalogowany użytkownik mógł korzystać ze strony rejestracji witryny. W takim przypadku, można zrobić coś takiego, opiera się istniejący login_required dekorator

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 
+0

Użyj "u.is_anonymous()" zamiast "nie u.is_authenticated()" . Zobacz https://docs.djangoproject.com/en/dev/ref/contrib/auth/#django.contrib.auth.models.AnonymousUser, aby uzyskać więcej informacji. – Krozark

1

Trochę późno, ale inny prosty sposób na rozwiązanie tego problemu będzie polegać na innym dekoratora i dodać funkcję lambda:

from django.contrib.auth.decorators import user_passes_test 

@user_passes_test(lambda u: u.is_anonymous) 
+0

Co do diaska: "edycje muszą mieć co najmniej 6 znaków, czy jest coś, co można poprawić w tym poście?" Do diabła, nie! W "u.is_anonymous" brakuje tylko nawiasów klamrowych, więc przykład jest niepoprawny. – simplylizz

+0

Przykład jest poprawny i działa. Gdzie chciałbyś umieścić dodatkowe nawiasy? –

+0

Och, sprawdziłem dokumenty. Masz rację, to prawda, ponieważ django 1.10+ 'is_anonymous' stało się atrybutem. Ale w poprzednich wersjach Django da ci zawsze "True", ponieważ metoda 'is_anonymous' była metodą. – simplylizz

1

@permission_classes ([permissions.AllowAny])

Powiązane problemy