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

多Django项目基于自定义用户模型的跨项目认证集成方案问询

多Django项目共享自定义User模型解决方案

问题背景

我拥有三个Django项目:Project A(认证项目)、Project B(用户相关功能管理)、Project C(餐厅业务处理)。已在Project A中实现符合需求的自定义User模型,需要达成以下目标:

  • 用户认证:通过Project A的自定义User模型验证访问Project B、C API的用户身份
  • 用户信息共享:在Project B、C中基于该模型实现用户详情查询、资料更新等操作
  • 数据一致性:确保Project A中User模型的变更同步到Project B、C

Project A的自定义User模型代码如下:

import uuid

from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin
from django.db import models
from django.utils import timezone


class UserManager(BaseUserManager):

    def _create_user(self, email, password, is_staff, is_superuser, **extra_fields):
        if not email:
            raise ValueError('Users must have an email address')
        now = timezone.now()
        email = self.normalize_email(email)
        user = self.model(
            email=email,
            is_staff=is_staff,
            is_active=True,
            is_superuser=is_superuser,
            last_login=now,
            date_joined=now,
            **extra_fields
        )
        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_user(self, email, password, **extra_fields):
        return self._create_user(email, password, False, False, **extra_fields)

    def create_superuser(self, email, password, **extra_fields):
        user = self._create_user(email, password, True, True, **extra_fields)
        return user


class User(AbstractBaseUser, PermissionsMixin):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    # username = models.CharField(max_length=255, null=True, blank=True, unique=True)
    email = models.EmailField(max_length=254)
    name = models.CharField(max_length=254, null=True, blank=True)
    is_staff = models.BooleanField(default=False)
    is_superuser = models.BooleanField(default=False)
    is_active = models.BooleanField(default=True)
    last_login = models.DateTimeField(null=True, blank=True)
    date_joined = models.DateTimeField(auto_now_add=True)
    organization_id = models.UUIDField(null=True, blank=True)

    USERNAME_FIELD = 'email'
    EMAIL_FIELD = 'email'
    REQUIRED_FIELDS = []

    objects = UserManager()

    def get_absolute_url(self):
        return "/users/%i/" % (self.pk)

可行方案:共享数据库表完整示例

你尝试的共享数据库表方案完全可行,以下是完整实现步骤:

1. 配置Project B、C连接共享数据库

将Project B、C的数据库配置指向Project A使用的数据库(或专门的共享数据库),确保三个项目访问同一数据库实例。修改Project B/C的settings.py:

# Project B/C settings.py
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',  # 替换为你的数据库引擎(如mysql/sqlite)
        'NAME': 'project_a_db',  # Project A的数据库名称
        'USER': 'your_db_user',
        'PASSWORD': 'your_db_password',
        'HOST': 'localhost',  # 数据库服务器地址
        'PORT': '5432',  # 对应数据库端口
    }
}

2. 在Project B、C中同步User模型

在Project B、C中创建一个专门的app(如shared_auth),将Project A的UserManager和User模型代码完整复制到该app的models.py中,必须指定与Project A一致的数据库表名:

# Project B/C shared_auth/models.py
import uuid

from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin
from django.db import models
from django.utils import timezone


class UserManager(BaseUserManager):
    def _create_user(self, email, password, is_staff, is_superuser, **extra_fields):
        if not email:
            raise ValueError('Users must have an email address')
        now = timezone.now()
        email = self.normalize_email(email)
        user = self.model(
            email=email,
            is_staff=is_staff,
            is_active=True,
            is_superuser=is_superuser,
            last_login=now,
            date_joined=now,
            **extra_fields
        )
        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_user(self, email, password, **extra_fields):
        return self._create_user(email, password, False, False, **extra_fields)

    def create_superuser(self, email, password, **extra_fields):
        user = self._create_user(email, password, True, True, **extra_fields)
        return user


class User(AbstractBaseUser, PermissionsMixin):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    email = models.EmailField(max_length=254)
    name = models.CharField(max_length=254, null=True, blank=True)
    is_staff = models.BooleanField(default=False)
    is_superuser = models.BooleanField(default=False)
    is_active = models.BooleanField(default=True)
    last_login = models.DateTimeField(null=True, blank=True)
    date_joined = models.DateTimeField(auto_now_add=True)
    organization_id = models.UUIDField(null=True, blank=True)

    USERNAME_FIELD = 'email'
    EMAIL_FIELD = 'email'
    REQUIRED_FIELDS = []

    objects = UserManager()

    # 关键:指定与Project A中User模型完全相同的数据库表名
    class Meta:
        db_table = 'accounts_user'  # 替换为Project A中User模型实际对应的表名(格式为appname_modelname)

    def get_absolute_url(self):
        return "/users/%i/" % (self.pk)

然后在Project B/C的settings.py中配置默认用户模型:

# Project B/C settings.py
AUTH_USER_MODEL = 'shared_auth.User'
# 别忘了把shared_auth添加到INSTALLED_APPS
INSTALLED_APPS = [
    # ...其他app
    'shared_auth',
]

3. 实现用户认证(以JWT为例)

方案1:直接通过共享数据库验证

借助djangorestframework-simplejwt库,在Project B/C中直接验证用户身份:

# Project B/C views.py
from django.contrib.auth import authenticate
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework_simplejwt.tokens import RefreshToken

class LoginView(APIView):
    def post(self, request):
        email = request.data.get('email')
        password = request.data.get('password')
        user = authenticate(email=email, password=password)
        
        if user and user.is_active:
            refresh = RefreshToken.for_user(user)
            return Response({
                'refresh': str(refresh),
                'access': str(refresh.access_token),
            }, status=status.HTTP_200_OK)
        
        return Response({'error': '无效的账号或密码'}, status=status.HTTP_401_UNAUTHORIZED)

后续API视图只需添加IsAuthenticated权限类即可实现认证:

from rest_framework.permissions import IsAuthenticated

class SomeProtectedView(APIView):
    permission_classes = [IsAuthenticated]
    
    def get(self, request):
        return Response({'user_id': request.user.id, 'email': request.user.email})

方案2:微服务模式(调用Project A认证接口)

如果不想直接共享数据库,可让Project B/C调用Project A的认证接口获取token,再验证token有效性:

# Project B/C views.py
import requests
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status

class LoginView(APIView):
    def post(self, request):
        auth_api_url = "http://project-a-server/api/login/"
        response = requests.post(auth_api_url, data=request.data)
        
        return Response(response.json(), status=response.status_code)

4. 实现用户信息共享

在Project B/C中直接通过ORM操作共享的User模型,实现查询、更新等功能:

# Project B/C serializers.py
from rest_framework import serializers
from shared_auth.models import User

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ['id', 'email', 'name', 'organization_id']
# Project B/C views.py
from rest_framework import generics
from shared_auth.models import User
from .serializers import UserSerializer
from rest_framework.permissions import IsAuthenticated

class UserDetailView(generics.RetrieveUpdateAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer
    permission_classes = [IsAuthenticated]
    lookup_field = 'id'  # 因为主键是UUID,指定lookup字段

5. 保障数据一致性

  • 所有User模型的结构变更(添加字段、修改约束等)必须在Project A中执行迁移,Project B/C无需执行migrate命令,只需同步模型代码即可
  • 确保三个项目的Django版本、相关依赖(如django.contrib.auth)版本一致,避免ORM行为差异
  • 禁止在Project B/C中修改User模型的核心逻辑(如认证逻辑),统一由Project A维护

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 13:52:20