Der beste Weg, um Djangos Login zu machen, wurde standardmäßig benötigt

Ich arbeite an einer großen Django App, die überwiegende Mehrheit von denen erfordert eine Anmeldung zum Zugriff. Das bedeutet, dass alle unsere App, die wir gesprengt haben:

@login_required def view(...): 

Das ist gut, und es funktioniert super , solange wir uns daran erinnern, es überall hinzuzufügen ! Leider haben wir manchmal vergessen, und das Versagen ist oft nicht furchtbar offensichtlich. Wenn der einzige Link zu einer Ansicht auf einer @login_required Seite ist, dann sind Sie wahrscheinlich nicht zu bemerken, dass Sie diese Ansicht tatsächlich erreichen können, ohne sich einzuloggen. Aber die bösen Jungs könnten bemerken, was ein Problem ist.

Meine Idee war, das System umzukehren. Anstatt irgendetwas @login_required zu schreiben, stattdessen hätte ich so etwas wie:

 @public def public_view(...): 

Nur für die öffentlichen Sachen. Ich habe versucht, dies mit einigen Middleware zu implementieren und ich konnte nicht scheinen, um es zu arbeiten. Alles, was ich versuchte, drückte schlecht mit anderen Middleware, die wir benutzen, denke ich. Als nächstes habe ich versucht, etwas zu schreiben, um die URL-Muster zu durchlaufen, um zu überprüfen, dass alles, was nicht @public war, @login_required markiert wurde – zumindest dann würden wir einen schnellen Fehler bekommen, wenn wir etwas vergessen haben. Aber dann konnte ich nicht herausfinden, wie zu erzählen, ob @login_required auf eine Ansicht angewendet worden war …

Also, was ist der richtige Weg, dies zu tun? Danke für die Hilfe!

5 Solutions collect form web for “Der beste Weg, um Djangos Login zu machen, wurde standardmäßig benötigt”

Middleware kann Ihre beste Wette sein. Ich habe dieses Stück Code in der Vergangenheit verwendet, modifiziert von einem Snippet woanders gefunden:

 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 

Dann in settings.py, listen Sie die Basis-URLs, die Sie schützen möchten:

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

Solange Ihre Website URL-Konventionen für die zu authentifizierenden Seiten verfolgt, funktioniert dieses Modell. Wenn dies nicht ein Eins-zu-eins-Fit ist, können Sie wählen, um die Middleware zu ändern, um Ihren Umständen genauer zu entsprechen.

Was ich an diesem Ansatz mag – neben dem Entfernen der Notwendigkeit, die Codebasis mit @login_required Dekorateure zu @login_required – ist, dass, wenn sich das Authentifizierungsschema ändert, man einen Ort hat, um globale Änderungen vorzunehmen.

Es gibt eine Alternative zum Einlegen eines Dekorators auf jede Ansichtsfunktion. Sie können auch den login_required() Dekorator in die Datei urls.py . Während dies ist immer noch eine manuelle Aufgabe, zumindest haben Sie alles an einem Ort, was macht es einfacher zu auditieren.

z.B,

     Von my_views import home_view

     Urlpatterns = muster ('',
         # "Zuhause":
         (R '^ $', login_required (home_view), dict (template_name = 'my_site / home.html', items_per_page = 20)),
     )

Beachten Sie, dass View-Funktionen direkt benannt und direkt importiert werden, nicht als Strings.

Beachten Sie auch, dass dies mit jedem aufrufbaren View-Objekt, einschließlich Klassen funktioniert.

Es ist schwer, die eingebauten Annahmen in Django zu ändern, ohne die Art und Weise, wie url's ausgegeben werden, um Funktionen zu überarbeiten, zu überarbeiten.

Anstatt in Django Einbauten zu verspotten, hier ist ein Audit, das du benutzen kannst. Überprüfe einfach jede Ansichtsfunktion.

 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 

Führen Sie diese aus und prüfen Sie die Ausgabe für def ohne entsprechende Dekorateure.

Inspiriert von Ber's Antwort, schrieb ich ein kleines Snippet, das die login_required ersetzt, indem du alle URL-Rückrufe mit dem login_required Dekorator login_required . Dies funktioniert in Django 1.6.

 def login_required_patterns(*args, **kw): for pattern in patterns(*args, **kw): # This is a property that should return a callable, even if a string view name is given. callback = pattern.callback # No property setter is provided, so this will have to do. pattern._callback = login_required(callback) yield pattern 

Es funktioniert so (der Aufruf der list ist wegen der yield erforderlich).

 urlpatterns = list(login_required_patterns('', url(r'^$', home_view))) 

Du kannst das nicht wirklich gewinnen. Sie müssen lediglich eine Erklärung über die Berechtigungsanforderungen abgeben. Wo sonst würdest du diese Deklaration außer rechts durch die View-Funktion setzen?

Erwägen Sie, Ihre View-Funktionen durch aufrufbare Objekte zu ersetzen.

 class LoginViewFunction( object ): def __call__( self, request, *args, **kw ): p1 = self.login( request, *args, **kw ) if p1 is not None: return p1 return self.view( request, *args, **kw ) def login( self, request ) if not request.user.is_authenticated(): return HttpResponseRedirect('/login/?next=%s' % request.path) def view( self, request, *args, **kw ): raise NotImplementedError 

Sie machen dann Ihre Ansicht Funktionen Unterklassen von LoginViewFunction .

 class MyRealView( LoginViewFunction ): def view( self, request, *args, **kw ): .... the real work ... my_real_view = MyRealView() 

Es wird keine Codezeilen gespeichert. Und es hilft nicht dem "wir vergessen" Problem. Alles, was Sie tun können, ist, den Code zu prüfen, um sicherzustellen, dass die Ansichtsfunktionen Objekte sind. Von der richtigen Klasse.

Aber selbst dann wirst du nie wirklich wissen, dass jede View-Funktion ohne eine Unit-Test-Suite korrekt ist.

Python ist die beste Programmiersprache der Welt.