it-swarm.com.de

Django und Middleware, die request.user verwendet, ist immer anonym

Ich versuche, Middleware zu erstellen, die einige Felder für den Benutzer basierend auf der Subdomain usw. ändert.

Das einzige Problem ist, dass der request.user immer als AnonymousUser in die Middleware kommt, dann aber der richtige Benutzer in den Ansichten ist. Ich habe die Standard-Authentifizierungs- und Sitzungs-Middleware, die Django verwendet, in den Einstellungen belassen.

Es gibt hier eine ähnliche Frage: Django, request.user ist immer anonymer Benutzer Er beantwortet jedoch nicht die gesamte Frage, da ich keine anderen Authentifizierungsmethoden verwende und djangos-Authentifizierung zuvor ausgeführt wird Ich rufe meine eigene Middleware auf.

Gibt es eine Möglichkeit, während der Verwendung von DRF den request.user in die Middleware zu bekommen? Ich zeige hier einen Beispielcode:

class SampleMiddleware(object):

  def process_view(self, request, view_func, view_args, view_kwargs):
    #This will be AnonymousUser.  I need it to be the actual user making the request.
    print (request.user)    

  def process_response(self, request, response):
    return response

mit process_request:

class SampleMiddleware(object):

  def process_request(self, request):
    #This will be AnonymousUser.  I need it to be the actual user making the request.
    print (request.user)    

  def process_response(self, request, response):
    return response

Vielen Dank!

Jordan

21
Jordan

Hey Leute, ich habe dieses Problem gelöst, indem ich das DRF-Token von den Anforderungen erhalten und request.user an den mit diesem Modell verbundenen Benutzer lade. 

Ich hatte die standardmäßige Django-Authentifizierung und Sitzungs-Middleware, aber es scheint, als würde DRF die Token-Authentifizierung nach Middleware verwenden, um den Benutzer aufzulösen (alle Anfragen waren CORS-Anfragen, dies war möglicherweise der Grund). Hier ist meine aktualisierte Middleware-Klasse:

from re import sub
from rest_framework.authtoken.models import Token
from core.models import OrganizationRole, Organization, User

class OrganizationMiddleware(object):

  def process_view(self, request, view_func, view_args, view_kwargs):
    header_token = request.META.get('HTTP_AUTHORIZATION', None)
    if header_token is not None:
      try:
        token = sub('Token ', '', request.META.get('HTTP_AUTHORIZATION', None))
        token_obj = Token.objects.get(key = token)
        request.user = token_obj.user
      except Token.DoesNotExist:
        pass
    #This is now the correct user
    print (request.user)

Dies kann auch für process_view oder process_request verwendet werden.

Hoffentlich kann dies in Zukunft jemandem helfen.

22
Jordan

Kam heute durch das gleiche Problem.

TL; DR;

Überspringen Sie unten das Code-Beispiel


Erklärung

Die Sache ist, dass DRF ihren eigenen Fluss von Dingen hat, mitten in der Django-Anfrage Lebenszyklus .

Wenn also der normale Middleware-Fluss lautet:

  1. request_middleware (bevor mit der Bearbeitung der Anfrage begonnen wird)
  2. view_middleware (vor dem Aufruf der Ansicht)
  3. template_middleware (vor dem Rendern)
  4. response_middleware (vor der letzten Antwort)

DRF-Code überschreibt den Standard-Django-View-Code und führt ihren eigenen Code aus.

Im obigen Link können Sie sehen, dass sie die ursprüngliche Anfrage mit ihren eigenen Methoden umschließen, wobei eine dieser Methoden die DRF-Authentifizierung ist.

Zurück zu Ihrer Frage, dies ist der Grund, warum die Verwendung von request.user in einer Middleware verfrüht ist, da sie nur ihren Wert after view_middleware ** ausführt.

Die Lösung, mit der ich gegangen bin, ist, dass meine Middleware eine LazyObject. Gesetzt hat. Dies ist hilfreich, da mein Code (der tatsächliche DRF-ApiVIew) ausgeführt wird, wenn der tatsächliche Benutzer bereits durch DRF-Authentifizierung . [.____. .] Diese Lösung wurde hier vorgeschlagen zusammen mit einer Diskussion.

Es wäre vielleicht besser gewesen, wenn DRF eine bessere Möglichkeit hätte, ihre Funktionalität zu erweitern, aber dies scheint besser zu sein als die angebotene Lösung (sowohl hinsichtlich der Leistung als auch hinsichtlich der Lesbarkeit).


Codebeispiel

from Django.utils.functional import SimpleLazyObject

def get_actual_value(request):
    if request.user is None:
        return None

    return request.user #here should have value, so any code using request.user will work


class MyCustomMiddleware(object):
    def process_request(self, request):
        request.custom_prop = SimpleLazyObject(lambda: get_actual_value(request))
13
Daniel Dubovski

Basierend auf der sehr eleganten Lösung von Daniel Dubovski, hier ein Beispiel für Middleware für Django 1.11:

from Django.utils.functional import SimpleLazyObject
from organization.models import OrganizationMember
from Django.core.exceptions import ObjectDoesNotExist


def get_active_member(request):
    try:
        active_member = OrganizationMember.objects.get(user=request.user)
    except (ObjectDoesNotExist, TypeError):
        active_member = None
    return active_member


class OrganizationMiddleware(object):
    def __init__(self, get_response):
        self.get_response = get_response


    def __call__(self, request):
        # Code to be executed for each request before
        # the view (and later middleware) are called.

        request.active_member = SimpleLazyObject(lambda: get_active_member(request))

        response = self.get_response(request)

        # Code to be executed for each request/response after
        # the view is called.
        return response
4
Sune Kjærgård

Ich weiß, es ist nicht genau die Antwort auf die Frage, ob wir auf die Middleware-Frage zugreifen können, aber ich denke, es ist eine elegantere Lösung, die dieselbe Arbeit in der Middleware-VS ausführt, wie DRJ in seiner base view class . Zumindest für das, was ich brauchte, war es sinnvoller, hier hinzuzufügen. 

Grundsätzlich überschreibe ich einfach die Methode 'perform_authentication ()' vom DRF-Code, da ich mehr Dinge hinzufügen musste, die sich auf den aktuellen Benutzer in der Anfrage beziehen. Die Methode ruft ursprünglich "request.user" auf.

class MyGenericViewset(viewsets.GenericViewSet):

    def perform_authentication(self, request):
        request.user

        if request.user and request.user.is_authenticated():
            request.my_param1 = 'whatever'

Danach in Ihren eigenen Ansichten, anstatt APIView von DRF als übergeordnete Klasse festzulegen, legen Sie diese Klasse einfach als übergeordnete Klasse fest.

2
mrmuggles

Die Lösung von Daniel Dubovski ist in den meisten Fällen wahrscheinlich die beste.

Das Problem beim Lazy-Object-Ansatz ist, wenn Sie sich auf die Nebenwirkungen verlassen müssen. In meinem Fall brauche ich für jede Anfrage etwas zu erledigen, egal was passiert.

Wenn ich einen speziellen Wert wie request.custom_prop verwende, muss er für jede Anforderung ausgewertet werden, damit die Nebenwirkungen auftreten. Ich habe festgestellt, dass andere Personen request.user einstellen, aber es funktioniert nicht für mich, da einige Middleware- oder Authentifizierungsklassen diese Eigenschaft überschreiben.

Was ist, wenn DRF seine eigene Middleware unterstützt? Wo könnte ich es anschließen? Der einfachste Weg in meinem Fall (ich brauche nicht auf das request-Objekt zuzugreifen, nur der authentifizierte Benutzer) scheint sich in die Authentifizierungsklasse selbst einzuhaken:

from rest_framework.authentication import TokenAuthentication

class TokenAuthenticationWithSideffects(TokenAuthentication):

    def authenticate(self, request):
        user_auth_Tuple = super().authenticate(request)

        if user_auth_Tuple is None:
            return
        (user, token) = user_auth_Tuple

        # Do stuff with the user here!

        return (user, token)

Dann könnte ich diese Zeile einfach in meinen Einstellungen ersetzen:

REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": (
        #"rest_framework.authentication.TokenAuthentication",
        "my_project.authentication.TokenAuthenticationWithSideffects",
    ),
    # ...
}

Ich fördere diese Lösung nicht, aber vielleicht hilft sie jemand anderem.

Vorteile:

  • Es löst dieses spezifische Problem
  • Es gibt keine doppelte Authentifizierung
  • Pflegeleicht

Nachteile:

  • In der Produktion nicht getestet
  • Die Dinge passieren an einem unerwarteten Ort
  • Nebenwirkungen...
0
André Laszlo

Ich war mit den Lösungen da draußen nicht ganz zufrieden. Hier ist eine Lösung, die einige DRF-Interna verwendet, um sicherzustellen, dass die korrekte Authentifizierung in der Middleware angewendet wird, auch wenn die Ansicht über bestimmte Berechtigungsklassen verfügt. Es verwendet den Middleware-Hook process_view, der uns Zugriff auf die Ansicht gibt, die wir gerade erreichen:

class CustomTenantMiddleware():
    def process_view(self, request, view_func, view_args, view_kwargs):
        # DRF saves the class of the view function as the .cls property
        view_class = view_func.cls
        try:
            # We need to instantiate the class
            view = view_class()
            # And give it an action_map. It's not relevant for us, but otherwise it errors.
            view.action_map = {}
            # Here's our fully formed and authenticated (or not, depending on credentials) request
            request = view.initialize_request(request)
        except (AttributeError, TypeError):
            # Can't initialize the request from this view. Fallback to using default permission classes
            request = APIView().initialize_request(request)

        # Here the request is fully formed, with the correct permissions depending on the view.

Beachten Sie, dass dies nicht unnötig ist, sich zweimal zu authentifizieren. DRF wird sich gleich danach glücklich authentifizieren.

0
Gustav Wengel