Django多用户模型结合Simple JWT认证遇用户找不到问题求助
问题分析与解决方案
你的核心问题是Simple JWT 默认依赖 AUTH_USER_MODEL 指定的模型获取用户,而自定义认证后端的全局变量方案在并发环境下完全不可靠,导致非Staff用户请求时出现user_not_found错误。以下是针对性的修复方案:
一、核心问题拆解
- 全局变量
var_model致命缺陷:多线程/多进程的Django服务中,全局变量会被不同请求覆盖,get_user方法无法保证使用正确的用户模型。 - Simple JWT 认证逻辑:JWT解析后会调用认证后端的
get_user方法,但此时无法通过请求路径动态判断模型(并发场景下路径判断逻辑失效)。 AUTH_USER_MODEL局限性:该配置仅指定默认用户模型,无法支持多模型认证场景。
二、分步修复方案
1. 重构自定义认证后端(去掉全局变量)
修改authentication.py,新增带用户类型识别的JWT认证类,同时优化后端逻辑:
from .models import Staff, Customer, Dealer from rest_framework_simplejwt.authentication import JWTAuthentication from django.contrib.auth.backends import BaseBackend class MultiModelJWTAuthentication(JWTAuthentication): """自定义JWT认证,从token payload中读取用户类型""" def get_user(self, validated_token): user_id = validated_token[self.user_id_claim] # 从token中获取预先存入的用户类型 user_type = validated_token.get("user_type") # 映射用户类型到对应模型 model_map = { "staff": Staff, "customer": Customer, "dealer": Dealer } target_model = model_map.get(user_type) if not target_model: return None try: return target_model.objects.get(pk=user_id) except target_model.DoesNotExist: return None class MultiModelAuthBackend(BaseBackend): """多模型用户名密码认证后端""" def authenticate(self, request, email=None, password=None): if not email or not password: return None validated_email = email.lower() # 根据请求路径判断目标模型 if "admin" in request.path: model = Staff elif "customer" in request.path: model = Customer elif "dealer" in request.path: model = Dealer else: return None try: user = model.objects.get(email=validated_email) if user.check_password(password): return user except model.DoesNotExist: return None def get_user(self, user_id): # 无token时的降级方案:逐个尝试模型(效率较低,仅用于非JWT场景) for model in [Staff, Customer, Dealer]: try: return model.objects.get(pk=user_id) except model.DoesNotExist: continue return None
2. 修改Settings配置
替换默认JWT认证类,指定自定义后端:
AUTH_USER_MODEL = 'account.Staff' # 保留默认,不影响多模型逻辑 AUTHENTICATION_BACKENDS = ['account.authentication.MultiModelAuthBackend',] REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( 'account.authentication.MultiModelJWTAuthentication', # 替换为自定义JWT认证 ) } # 原SIMPLE_JWT配置保留,新增无需修改
3. 登录时注入用户类型到Token
在登录视图中生成JWT时,将用户类型存入Token的Payload:
from rest_framework_simplejwt.tokens import RefreshToken from rest_framework.response import Response from rest_framework import status def customer_login(request): # 示例:验证Customer用户密码 email = request.data.get("email") password = request.data.get("password") try: user = Customer.objects.get(email=email.lower()) if user.check_password(password): # 生成Token并注入用户类型 refresh = RefreshToken.for_user(user) refresh["user_type"] = "customer" # 存入用户类型标识 return Response({ "refresh": str(refresh), "access": str(refresh.access_token) }, status=status.HTTP_200_OK) else: return Response({"error": "密码错误"}, status=status.HTTP_400_BAD_REQUEST) except Customer.DoesNotExist: return Response({"error": "用户不存在"}, status=status.HTTP_404_NOT_FOUND) # Staff/Dealer登录视图同理,只需修改model和user_type值
4. 视图权限与序列化优化
修复视图中宽泛的异常捕获,添加用户类型校验:
from rest_framework.exceptions import PermissionDenied from rest_framework.permissions import BasePermission # 自定义权限类:仅允许Customer访问 class IsCustomer(BasePermission): def has_permission(self, request, view): return isinstance(request.user, Customer) class customer_profile_view(APIView): renderer_classes = [apirenderer] permission_classes = [IsAuthenticated, IsCustomer] # 添加自定义权限 def get(self, request, format=None): serializer_object = customer_profile_serializer(request.user) return Response(serializer_object.data, status=status.HTTP_200_OK)
三、关键注意事项
- 用户ID唯一性:确保三个用户模型的
id字段不会重复(可通过自定义主键前缀或数据库分表规避)。 - Token安全:用户类型存入Payload是安全的,因为JWT本身会签名,篡改后会验证失败。
- 权限隔离:通过自定义
IsCustomer/IsDealer权限类,严格控制不同用户类型的接口访问权限。
内容的提问来源于stack exchange,提问作者harshit
相关产品推荐
相关产品推荐

