使用request.user的Django和Middleware总是匿名的

时间:2014-10-07 16:30:51

标签: python django django-rest-framework

我正在尝试制作基于子域等改变用户某些字段的中间件......

唯一的问题是request.user总是在中间件中作为AnonymousUser出现,但是在视图中是正确的用户。我已经离开了django在设置中使用的默认身份验证和会话中间件。

这里有一个类似的问题:Django, request.user is always Anonymous User 但是并没有过度回答整个问题,因为我没有使用不同的身份验证方法,并且在调用我自己的中间件之前djangos身份验证正在运行。

在使用DRF时,有没有办法在中间件中获取request.user?我将在这里展示一些示例代码:

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

使用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

7 个答案:

答案 0 :(得分:26)

嘿伙计我通过从请求中获取DRF令牌并将request.user加载到与该模型关联的用户来解决此问题。

我有默认的django身份验证和会话中间件,但似乎DRF在中间件解析用户之后使用它的令牌身份验证(所有请求都是CORS请求,这可能就是原因)。这是我更新的中间件类:

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)

这也可以在process_view或process_request上使用。

希望将来可以帮助某人。

答案 1 :(得分:15)

今天碰到了这个问题。

<强> TL; DR;

跳过下面的代码示例

<强>解释

事情是DRF有自己的事物流,就在django请求life-cycle的中间。

因此,如果正常的中间件流程是:

  1. request_middleware(在开始处理请求之前)
  2. view_middleware(在调用视图之前)
  3. template_middleware(渲染前)
  4. response_middleware(在最终回复之前)
  5. DRF代码会覆盖默认的django视图代码,并执行their own code

    在上面的链接中,您可以看到它们使用自己的方法包装原始请求,其中一种方法是DRF身份验证。

    回到你的问题,这就是在中间件中使用request.user为时过早的原因,因为它只有在 view_middleware **执行后得到它的值

    我使用的解决方案是让我的中间件设置为LazyObject。 这有帮助,因为我的代码(实际的DRF ApiVIew)在实际用户已由DRF's authentication设置时执行。 这个解决方案是proposed here和讨论。

    如果DRF有更好的方法来扩展其功能,可能会更好,但事实上,这似乎比提供的解决方案更好(性能和可读性都明智)。

    代码示例

    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))
    

答案 2 :(得分:4)

基于Daniel Dubovski上面非常优雅的解决方案,这里是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

答案 3 :(得分:3)

我知道它并没有完全回答“我们可以从中间件访问”吗?&#39;问题,但我认为这是一个更优雅的解决方案VS在中间件VS中做同样的工作VS DRJ在其基础view class中做了什么。至少对于我需要的东西,在这里添加更有意义。

基本上,我只是重写方法&#39; perform_authentication()&#39;来自DRF的代码,因为我需要在请求中添加与当前用户相关的更多内容。该方法最初调用了#request; user&#39;。

class MyGenericViewset(viewsets.GenericViewSet):

    def perform_authentication(self, request):
        request.user

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

在您自己的视图中,而不是将DRF中的APIView设置为父类,只需将该类设置为父类。

答案 4 :(得分:0)

Daniel Dubovski's solution在大多数情况下可能是最好的。

惰性对象方法的问题在于是否需要依赖副作用。就我而言,无论什么情况,我都需要为每个请求做一些事情。

如果我使用特殊的值,例如request.custom_prop,则必须针对每次发生的副作用进行评估。我注意到了other people are setting request.user,但对我来说不起作用,因为某些中间件或身份验证类会覆盖此属性。

如果DRF支持自己的中间件怎么办?我可以在哪里插入?在我的情况下,最简单的方法(我不需要访问request对象,只需要经过身份验证的用户)似乎是挂接到身份验证类本身上的:

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)

然后我可以在设置中替换此行:

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

我不会推广此解决方案,但也许会帮助其他人。

优点:

  • 解决这个特定问题
  • 没有双重身份验证
  • 易于维护

缺点:

  • 未经生产测试
  • 事情发生在意想不到的地方
  • 副作用...

答案 5 :(得分:0)

我对那里的解决方案不满意。这是一个使用某些DRF内部结构的解决方案,即使视图具有特定的权限类,也要确保在中间件中应用了正确的身份验证。它使用中间件钩子process_view,该钩子使我们可以访问将要命中的视图:

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.

请注意,这不能避免必须进行两次身份验证。此后,DRF仍将很高兴进行身份验证。

答案 6 :(得分:0)

我遇到了同样的问题,因此决定更改设计。我只是使用猴子补丁rest_framework.views.APIView而不是使用中间件。

就我而言,我需要修补check_permissions,但是您可以修补所有适合您的问题的修补程序。看看the source code

settings.py

INSTALLED_APPS = [
    ..
    'myapp',
]

myapp / patching.py

import sys

from rest_framework.views import APIView as OriginalAPIView


class PatchedAPIView(OriginalAPIView):
    def check_permissions(self, request):
        print(f"We should do something with user {request.user}"
        return OriginalAPIView.check_permissions(self, request)


# We replace the Django REST view with our patched one
sys.modules['rest_framework'].views.APIView = PatchedAPIView

myapp / __ init __。py

from .patching import *