Django+PostgreSQL独立Schema多租户:认证与AUTH_USER_MODEL如何处理?
Django多租户SaaS分离认证与用户模型解决方案
一、基类继承方案(可行且推荐)
你考虑的基类继承方案完全可行,具体实现思路如下:
- 定义继承
AbstractUser的抽象基类BaseUser,只保留通用字段(如用户名、邮箱、密码),设置abstract = True,不作为AUTH_USER_MODEL。 - 创建两个子类:
GlobalAdmin(存储在public数据库,负责系统管理)和TenantUser(存储在对应租户Schema,关联租户内其他业务表),各自添加专属字段(比如GlobalAdmin加系统权限字段,TenantUser加租户关联字段)。 - 配置Django数据库路由,确保
GlobalAdmin始终路由到public数据库,TenantUser根据当前租户切换到对应Schema。
代码示例:
# models.py from django.contrib.auth.models import AbstractUser from django.db import models class BaseUser(AbstractUser): class Meta: abstract = True class GlobalAdmin(BaseUser): system_permissions = models.JSONField(default=dict) class Meta: db_table = 'global_admin' class TenantUser(BaseUser): tenant_profile = models.ForeignKey('TenantProfile', on_delete=models.CASCADE) class Meta: db_table = 'tenant_user'
二、自定义认证后端(核心解决多模型认证问题)
Django允许配置多个认证后端,通过自定义后端实现分离认证逻辑,无需依赖单一AUTH_USER_MODEL:
- 编写两个认证后端:
GlobalAdminAuthBackend和TenantUserAuthBackend,分别处理两类用户的认证。 GlobalAdminAuthBackend直接操作public库的GlobalAdmin模型验证账号密码。TenantUserAuthBackend先通过请求中的租户标识(如子域名、请求头)确定租户Schema,切换数据库连接后验证TenantUser的账号密码。- 在
settings.py中注册这两个后端,注意顺序(全局管理员后端可优先)。
代码示例:
# backends.py from django.contrib.auth.backends import ModelBackend from .models import GlobalAdmin, TenantUser from django.db import connections class GlobalAdminAuthBackend(ModelBackend): def authenticate(self, request, username=None, password=None, **kwargs): try: # 指定使用public数据库 user = GlobalAdmin.objects.using('public').get(username=username) if user.check_password(password): return user except GlobalAdmin.DoesNotExist: return None class TenantUserAuthBackend(ModelBackend): def authenticate(self, request, username=None, password=None, **kwargs): # 从子域名提取租户标识 tenant_id = request.get_host().split('.')[0] # 切换到对应租户Schema with connections['tenant'].schema_editor() as editor: editor.set_schema(tenant_id) try: user = TenantUser.objects.using('tenant').get(username=username) if user.check_password(password): return user except TenantUser.DoesNotExist: return None
settings配置:
AUTHENTICATION_BACKENDS = [ 'yourapp.backends.GlobalAdminAuthBackend', 'yourapp.backends.TenantUserAuthBackend', ] # 无需设置AUTH_USER_MODEL,由后端直接处理不同模型
三、路由级别的数据库隔离优化
将全局管理路由与租户路由分离,通过中间件提前切换数据库/Schema:
- 全局管理员路由前缀设为
/admin/,租户路由前缀设为/tenant/或使用独立子域名。 - 编写中间件,根据请求路径或子域名判断请求类型,提前切换到对应数据库连接,避免认证后端重复切换操作。
四、关键注意事项
- 数据库路由需精准配置,确保
GlobalAdmin始终指向public库,TenantUser动态路由到对应租户Schema。 - 区分两类用户的登录入口,比如全局管理员用
/admin/login/,租户用户用/tenant/login/,避免认证逻辑冲突。 - 权限控制分离:全局管理员使用Django默认权限系统,租户用户的权限在各自Schema内独立管理。
内容的提问来源于stack exchange,提问作者Br0k3nS0u1
相关产品推荐
相关产品推荐

