多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
相关产品推荐
相关产品推荐

