首页 文章

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

提问于
浏览
20

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

唯一的问题是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

谢谢!

约旦

5 回答

  • 20

    基于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
    
  • 4

    嘿伙计我通过从请求中获取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上使用 .

    希望这可以在将来帮助某人 .

  • 2

    在大多数情况下,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",
        ),
        # ...
    }
    

    我不推广这个解决方案,但也许它会帮助别人 .

    Pros:

    • 它解决了这个具体问题

    • 没有双重身份验证

    • 易于维护

    Cons:

    • 未在 生产环境 中测试过

    • 事情发生在意想不到的地方

    • 副作用......

  • 13

    我知道's not exactly answering the '我们可以从中间件' question, but I think it'访问它是一个更优雅的解决方案VS在中间件VS中做同样的工作VS DRJ在其基础view class中做了什么 . 至少在我需要的地方,添加到这里更有意义 .

    基本上,我只是从DRF的代码中覆盖了'perform_authentication()'方法,因为我需要在请求中添加与当前用户相关的更多内容 . 该方法最初调用'request.user' .

    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设置为父类,只需将该类设置为父类 .

  • 0

    遇到同样的问题,今天遇到了这个问题 .

    TL;DR;

    跳过下面的代码示例


    Explanation

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

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

    • request_middleware(在开始处理请求之前)

    • view_middleware(在调用视图之前)

    • template_middleware(渲染前)

    • response_middleware(在最终回复之前)

    DRF代码覆盖默认的django视图代码,并执行their own code .

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

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

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

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


    Code Example

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

相关问题