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

如何基于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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 02:35:17