You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django多用户模型结合Simple JWT认证遇用户找不到问题求助

问题分析与解决方案

你的核心问题是Simple JWT 默认依赖 AUTH_USER_MODEL 指定的模型获取用户,而自定义认证后端的全局变量方案在并发环境下完全不可靠,导致非Staff用户请求时出现user_not_found错误。以下是针对性的修复方案:


一、核心问题拆解

  1. 全局变量 var_model 致命缺陷:多线程/多进程的Django服务中,全局变量会被不同请求覆盖,get_user方法无法保证使用正确的用户模型。
  2. Simple JWT 认证逻辑:JWT解析后会调用认证后端的get_user方法,但此时无法通过请求路径动态判断模型(并发场景下路径判断逻辑失效)。
  3. 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)

三、关键注意事项

  1. 用户ID唯一性:确保三个用户模型的id字段不会重复(可通过自定义主键前缀或数据库分表规避)。
  2. Token安全:用户类型存入Payload是安全的,因为JWT本身会签名,篡改后会验证失败。
  3. 权限隔离:通过自定义IsCustomer/IsDealer权限类,严格控制不同用户类型的接口访问权限。

内容的提问来源于stack exchange,提问作者harshit

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 22:55:18