Django中间件无法获取登录用户名的问题及方案咨询
request.user.username的问题 问题背景
我尝试在以下Django中间件中获取request.user.username:
class MyMiddleware(): def __init__(self, get_response): self.get_response = get_response def __call__(self, request): response = self.get_response(request) user_id = request.user.username # ...
我已确保MyMiddleware在settings.py中位于SessionMiddleware和AuthenticationMiddleware之后:
MIDDLEWARE = ( 'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', # ... 'my_app.my_py_file.MyMiddleware' )
但request.user.username仍为空字符串。
Q1. 这是为什么?
我还尝试添加@authentication_classes([TokenAuthentication,]):
from rest_framework.authentication import TokenAuthentication # <-- from rest_framework.decorators import authentication_classes # <-- @authentication_classes([TokenAuthentication,]) # <-- class MyMiddleware(): def __init__(self, get_response): self.get_response = get_response def __call__(self, request): response = self.get_response(request) user_id = request.user.username # ...
但没有效果。
我还尝试让类继承APIView(尽管中间件不是REST API,这看起来很不合理):
from rest_framework.authentication import TokenAuthentication # <-- from rest_framework.views import APIView # <-- class MyMiddleware(APIView): # <-- authentication_classes = [TokenAuthentication] # <-- def __init__(self, get_response): self.get_response = get_response def __call__(self, request): response = self.get_response(request) user_id = request.user.username # ...
仍然无效。
之后我看到一种说法:
如果你使用simple_jwt,无法在中间件中直接使用request.user,因为认证机制在视图函数中运行。因此你需要在中间件中手动认证,之后才能使用request.user获取用户模型中的数据。
该方案使用了rest_framework_simplejwt,它不属于Django框架,需要单独安装。
目前我能在中间件中获取用户名的唯一方法是参考一种方案,修改后的中间件如下:
from re import sub from rest_framework.authtoken.models import Token class MyMiddleware(object): def __init__(self, get_response): self.get_response = get_response def __call__(self, request): response = self.get_response(request) user_id = request.user.username if not user_id: user_id = self.getUsername(request) # ... def getUsername(self, request): header_token = request.META.get('HTTP_AUTHORIZATION', None) if header_token is not None: try: token = sub('Token ', '', header_token) token_obj = Token.objects.get(key = token) except Token.DoesNotExist: pass return token_obj.user.username
Q2. 这是不安装第三方库的情况下,在中间件中获取用户名的唯一且正确的方法吗?
更新
以下是我的CustomUser类及相关方法create_custom_user():
class CustomUser(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE) # 一些应用特定字段 @receiver(post_save, sender=User) @debugger_queries def create_custom_user(sender, **kwargs): instance = kwargs['instance'] if kwargs['created']: # 创建 CustomUser.objects.get_or_create(user=instance)
回答
针对Q1:为什么request.user.username为空
- 认证时机不匹配:Django自带的
AuthenticationMiddleware仅处理基于Session的Web登录认证。如果你的接口用的是DRF的TokenAuthentication这类token认证,DRF的认证逻辑是在视图层执行的,中间件运行时视图还未处理,此时request.user还是匿名用户(AnonymousUser),所以username为空。 - 视图层配置对中间件无效:
@authentication_classes装饰器和APIView都是DRF视图专属的机制,中间件不属于视图范畴,这些配置对中间件完全不起作用,因此你的尝试没有效果。
针对Q2:不装第三方库的情况下是否有其他方法
你当前手动解析Token的方法可行,但不是唯一方案,还有更规范的实现方式:
方法1:复用DRF的TokenAuthentication逻辑
直接调用DRF的认证类完成验证,避免自己写正则解析token,减少出错概率:
from rest_framework.authentication import TokenAuthentication from rest_framework.exceptions import AuthenticationFailed class MyMiddleware: def __init__(self, get_response): self.get_response = get_response self.auth = TokenAuthentication() def __call__(self, request): # 认证逻辑要放在调用get_response之前执行 try: user, token = self.auth.authenticate(request) if user: request.user = user except AuthenticationFailed: # 认证失败时保持request.user为匿名用户,不影响后续流程 pass response = self.get_response(request) # 此时可正常获取request.user.username user_id = request.user.username # ... return response
方法2:完善自定义Token认证逻辑
如果不想依赖DRF的认证类,可自己实现,但要补充异常处理(避免token不存在时引发报错):
from re import sub from rest_framework.authtoken.models import Token class MyMiddleware: def __init__(self, get_response): self.get_response = get_response def __call__(self, request): self.authenticate(request) response = self.get_response(request) user_id = request.user.username # ... return response def authenticate(self, request): header_token = request.META.get('HTTP_AUTHORIZATION', '') if not header_token.startswith('Token '): return try: token_key = sub('^Token ', '', header_token) token_obj = Token.objects.get(key=token_key) request.user = token_obj.user except Token.DoesNotExist: # token不存在时不修改request.user,保持匿名状态 pass
注意要把认证逻辑放在get_response(request)之前,这样后续视图和其他中间件也能使用已认证的用户信息。
额外说明
你的CustomUser是基于Django自带User的一对一扩展,这和中间件获取用户名的问题无关,只要token_obj.user能关联到正确的User实例,就能正常获取username。
内容的提问来源于stack exchange,提问作者MsA

