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

Jor*_*dan 29 python django django-rest-framework

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

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

这里有一个类似的问题:Django,request.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
Run Code Online (Sandbox Code Playgroud)

使用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
Run Code Online (Sandbox Code Playgroud)

Jor*_*dan 31

嘿伙计我通过从请求中获取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)
Run Code Online (Sandbox Code Playgroud)

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

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

  • 嘿,所以我发现这不是实现这个的最好方法.实现它的正确方法是将rest_frameworks身份验证中间件包含在设置中的AUTHENTICATION_BACKENDS中.```"rest_framework.authentication.TokenAuthentication'``` (3认同)

Dan*_*ski 18

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

TL; DR;

跳过下面的代码示例


说明

事情是DRF有自己的事物流,正好在django请求生命周期的中间.

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

  1. request_middleware(在开始处理请求之前)
  2. view_middleware(在调用视图之前)
  3. template_middleware(渲染前)
  4. response_middleware(最终回复之前)

DRF代码覆盖默认的django视图代码,并执行自己的代码.

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

回到你的问题,这就是request.user在中间件中使用是不成熟的原因,因为它只 view_middleware**执行得到它的值.

我使用的解决方案是让我的中间件设置为LazyObject.这有帮助,因为我的代码(实际的DRF ApiVIew)在DRF的身份验证已经设置了实际用户时执行.这里提出了这个解决方案和讨论.

如果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))
Run Code Online (Sandbox Code Playgroud)

  • 我得到`object()不带参数` (2认同)

hoe*_*ing 12

接受的答案仅考虑TokenAuthentication- 就我而言,配置了更多身份验证方法。因此,我直接初始化 DRF Request,这会调用 DRF 的身份验证机制并循环遍历所有配置的身份验证方法。

不幸的是,它仍然给数据库带来了额外的负载,因为Token必须查询对象(接受的答案也有这个问题)。这个答案SimpleLazyObject中的技巧是一个更好的解决方案,但它不适用于我的用例,因为我直接需要中间件中的用户信息 - 我正在扩展指标并在调用之前处理请求。django_prometheusget_response

from rest_framework.request import Request as RestFrameworkRequest
from rest_framework.views import APIView

class MyMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        drf_request: RestFrameworkRequest = APIView().initialize_request(request)
        user = drf_request.user
        ...
        return self.get_response(request)
Run Code Online (Sandbox Code Playgroud)

  • 与 SimpleJWT 配合使用 (2认同)
  • 我最初选择了这个解决方案,因为它是最简洁的解决方案。但它导致了一些问题:1)登录到 Django 管理员(用户始终是匿名且从不登录)2)当实现自定义 DRF 身份验证类时,中间件在身份验证失败时会导致 500 个响应。 (2认同)

Sun*_*ård 7

基于上面 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
Run Code Online (Sandbox Code Playgroud)


And*_*zlo 5

在大多数情况下,丹尼尔·杜博夫斯基的解决方案可能是最好的。

惰性对象方法的问题是您是否需要依赖副作用。就我而言,无论如何,我都需要为每个请求发生一些事情。

如果我使用像 这样的特殊值request.custom_prop,则必须针对每个请求进行评估,以免产生副作用。我注意到其他人正在设置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)
Run Code Online (Sandbox Code Playgroud)

然后我可以在我的设置中替换这一行:

REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": (
        #"rest_framework.authentication.TokenAuthentication",
        "my_project.authentication.TokenAuthenticationWithSideffects",
    ),
    # ...
}
Run Code Online (Sandbox Code Playgroud)

我不是在推广这个解决方案,但也许它会对其他人有所帮助。

优点:

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

缺点:

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