如何基于django_auth_adfs为Django多租户SSO动态修改认证参数
多租户场景下动态配置django_auth_adfs的实现方案
由于Django启动后settings.py是只读状态,无法直接动态修改其中的硬编码参数,我们需要通过自定义认证后端+数据库存储租户配置的方式实现动态适配不同租户的ADFS参数。
步骤1:存储租户ADFS配置到数据库
首先创建一个模型来保存每个租户的ADFS相关参数,方便动态读取:
# your_app/models.py from django.db import models class TenantADFSConfig(models.Model): tenant_id = models.CharField(max_length=255, unique=True, verbose_name="租户ID") client_id = models.CharField(max_length=255, verbose_name="客户端ID") client_secret = models.CharField(max_length=255, verbose_name="客户端密钥") domain = models.CharField(max_length=255, unique=True, verbose_name="租户标识域名") adfs_server_url = models.CharField(max_length=255, default="https://login.microsoftonline.com") def __str__(self): return self.domain
执行迁移命令将模型同步到数据库:
python manage.py makemigrations python manage.py migrate
之后可以通过后台管理页面或脚本添加各租户的配置数据。
步骤2:自定义多租户ADFS认证后端
继承django_auth_adfs的默认认证后端,重写认证逻辑,动态加载当前租户的配置:
# your_app/backends.py from django_auth_adfs.backends import AdfsAuthCodeBackend from django.core.exceptions import ObjectDoesNotExist from .models import TenantADFSConfig class MultiTenantAdfsBackend(AdfsAuthCodeBackend): def authenticate(self, request, code=None, redirect_uri=None, **kwargs): # 从请求中识别租户(这里以请求域名为例,可根据实际场景调整) # 示例:从host提取租户域名,如请求来自sso.tenant1.com,提取tenant1.com host_parts = request.get_host().split('.') tenant_domain = f"{host_parts[-2]}.{host_parts[-1]}" if len(host_parts)>=2 else request.get_host() try: tenant_config = TenantADFSConfig.objects.get(domain=tenant_domain) except ObjectDoesNotExist: # 租户不存在时返回None,触发后续认证后端(如果有) return None # 动态替换ADFS配置参数 self.adfs_config = { "CLIENT_ID": tenant_config.client_id, "CLIENT_SECRET": tenant_config.client_secret, "TENANT_ID": tenant_config.tenant_id, "SERVER": f"{tenant_config.adfs_server_url}/{tenant_config.tenant_id}", "RELYING_PARTY_ID": request.build_absolute_uri('/'), "AUDIENCE": f"microsoft:identityserver:{tenant_config.client_id}", "SCOPE": "openid email profile", # 可根据需求补充其他django_auth_adfs配置项 } # 调用父类的认证逻辑完成登录 return super().authenticate(request, code=code, redirect_uri=redirect_uri, **kwargs)
步骤3:替换默认认证后端
修改settings.py,将默认的ADFS认证后端替换为自定义的多租户版本:
# settings.py AUTHENTICATION_BACKENDS = [ 'your_app.backends.MultiTenantAdfsBackend', # 保留Django默认后端(可选,用于本地用户登录) 'django.contrib.auth.backends.ModelBackend', ] # 注释掉原有的硬编码ADFS配置,改为动态加载 # ADFS = { # "CLIENT_ID": "...", # ... # }
步骤4:自定义租户登录入口
由于不同租户的ADFS授权地址不同,需要自定义登录视图生成对应租户的授权链接:
# your_app/views.py from django.shortcuts import redirect from urllib.parse import urlencode from .models import TenantADFSConfig from django.core.exceptions import ObjectDoesNotExist from django.http import HttpResponseNotFound def multi_tenant_login(request): # 同认证后端的租户识别逻辑 host_parts = request.get_host().split('.') tenant_domain = f"{host_parts[-2]}.{host_parts[-1]}" if len(host_parts)>=2 else request.get_host() try: tenant_config = TenantADFSConfig.objects.get(domain=tenant_domain) except ObjectDoesNotExist: return HttpResponseNotFound("当前租户未配置ADFS登录") # 构造ADFS授权URL auth_base_url = f"{tenant_config.adfs_server_url}/{tenant_config.tenant_id}/oauth2/v2.0/authorize" auth_params = { 'client_id': tenant_config.client_id, 'response_type': 'code', 'redirect_uri': request.build_absolute_uri('/adfs/callback'), 'scope': 'openid email profile', 'response_mode': 'query', } auth_url = f"{auth_base_url}?{urlencode(auth_params)}" return redirect(auth_url)
步骤5:配置URL路由
将自定义登录视图和ADFS回调视图添加到路由:
# your_app/urls.py from django.urls import path from .views import multi_tenant_login from django_auth_adfs.views import OAuth2CallbackView urlpatterns = [ path('login/', multi_tenant_login, name='multi_tenant_adfs_login'), path('adfs/callback/', OAuth2CallbackView.as_view(), name='adfs_callback'), # 其他路由... ]
关键注意事项
- 租户识别逻辑:示例中用请求域名识别租户,你可以根据实际场景调整(如URL路径、请求参数、用户邮箱后缀等)。
- 密钥安全性:数据库中存储的
client_secret建议加密,可使用django-encrypted-model-fields等工具对字段加密。 - 令牌刷新:如果需要支持令牌自动刷新,需重写后端的
refresh_token方法,确保刷新时使用对应租户的配置。
内容的提问来源于stack exchange,提问作者Akram
相关产品推荐
相关产品推荐

