基于子域名的django-tenants多租户应用迁移至单URL架构可行性咨询
解决方案:从django-tenants子域名多租户迁移到单URL架构
核心思路
要实现单URL下的租户隔离+统一认证导航,关键是在会话中持久化租户标识,同时让django-tenants能从会话而非子域名识别租户。
具体实现步骤
1. 修改租户中间件逻辑
django-tenants默认通过子域名解析租户,需要自定义中间件替换TenantMiddleware,优先从会话中获取租户schema:
from django_tenants.middleware.main import TenantMiddleware from django_tenants.utils import get_tenant_model class SessionBasedTenantMiddleware(TenantMiddleware): def get_tenant(self, request): # 先从会话取租户标识,没有再尝试子域名(兼容过渡) tenant_schema = request.session.get('tenant_schema') if tenant_schema: TenantModel = get_tenant_model() try: return TenantModel.objects.get(schema_name=tenant_schema) except TenantModel.DoesNotExist: pass # fallback到原逻辑(可选,用于过渡阶段) return super().get_tenant(request)
然后在settings.py替换原中间件:
MIDDLEWARE = [ # ... 其他中间件 # 'django_tenants.middleware.main.TenantMiddleware', 注释掉原中间件 'yourapp.middleware.SessionBasedTenantMiddleware', # 换成自定义的 ]
2. 统一认证入口与会话绑定租户
- 设计统一的登录页面(放在公共schema的app里),让用户输入租户标识(如租户代码/名称)+账号密码。
- 认证时先根据租户标识找到对应schema,切换schema后验证用户凭据:
from django_tenants.utils import schema_context from django.contrib.auth import authenticate, login from django_tenants.models import Tenant def unified_login(request): if request.method == 'POST': tenant_code = request.POST.get('tenant_code') username = request.POST.get('username') password = request.POST.get('password') try: tenant = Tenant.objects.get(client_name=tenant_code) # 或按schema_name查询 with schema_context(tenant.schema_name): user = authenticate(request, username=username, password=password) if user: login(request, user) # 把租户schema存入会话 request.session['tenant_schema'] = tenant.schema_name return redirect('tenant_dashboard') # 跳转到租户内首页 else: return render(request, 'login.html', {'error': '账号密码错误'}) except Tenant.DoesNotExist: return render(request, 'login.html', {'error': '租户不存在'}) return render(request, 'login.html')
- 注意:登录视图必须放在公共schema的应用中,确保所有租户都能访问。
3. 处理租户内导航的会话维持
- 所有租户内的视图都会依赖上述中间件,会话中的
tenant_schema会自动让django-tenants切换到对应schema,无需在URL中携带schema参数。 - 退出登录时清除会话中的租户标识:
def unified_logout(request): request.session.pop('tenant_schema', None) logout(request) return redirect('unified_login')
4. 解决会话跨schema的潜在问题
django-tenants的会话默认存储在公共schema的django_session表中(默认配置),无需额外处理。如果之前会话存在租户私有schema,需要迁移会话数据到公共schema,确保登录态能跨schema识别。
关键注意事项
- 租户标识的安全性:要验证用户输入的租户标识合法性,防止租户越权访问;可在登录时绑定用户与租户的关联关系,确保用户只能登录自己所属的租户。
- 中间件顺序:自定义租户中间件要放在
AuthenticationMiddleware之后,确保会话已初始化后再解析租户。 - 静态资源与媒体文件:如果租户有独立的静态/媒体资源,需要通过视图动态返回,或在URL中通过租户标识区分(但无需暴露schema名称)。
内容的提问来源于stack exchange,提问作者Gabriel dos Santos
相关产品推荐
相关产品推荐

