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

基于子域名用不同Django密钥生成多租户JWT的实现疑问

多租户子域名场景下用django-rest-framework-simplejwt自定义密钥生成Token的解决方案

核心思路

根据请求的子域名匹配对应租户的密钥,在Token生成和验证流程中替换全局密钥,无需重新初始化API。

具体实现步骤

1. 编写租户密钥获取函数

从请求的子域名提取租户标识,读取对应环境文件中的密钥:

import os
from django.conf import settings

def get_tenant_secret(request):
    host = request.get_host()
    # 按实际域名结构提取子域名,示例为tenant1.local -> tenant1
    subdomain = host.split('.')[0]
    env_file_path = os.path.join(settings.BASE_DIR, f'.{subdomain}.local.env')
    
    if os.path.exists(env_file_path):
        with open(env_file_path, 'r') as env_file:
            for line in env_file:
                stripped_line = line.strip()
                if stripped_line.startswith('DJANGO_SECRET_KEY='):
                    return stripped_line.split('=', 1)[1]
    #  fallback到全局密钥,避免密钥不存在导致服务异常
    return settings.SECRET_KEY

2. 重写Token生成序列化器

替换Token生成时使用的密钥,覆盖TokenObtainPairSerializer的逻辑:

from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
from rest_framework_simplejwt.tokens import RefreshToken

class TenantTokenObtainPairSerializer(TokenObtainPairSerializer):
    @classmethod
    def get_token(cls, user):
        request = cls.context.get('request')
        tenant_secret = get_tenant_secret(request)
        
        # 生成RefreshToken并替换其加密密钥
        refresh_token = RefreshToken.for_user(user)
        refresh_token.encoder.secret_key = tenant_secret
        
        # AccessToken会关联到RefreshToken,无需单独设置密钥
        return refresh_token

    def validate(self, attrs):
        # 继承父类验证逻辑,可按需追加租户相关字段返回
        data = super().validate(attrs)
        return data

3. 重写登录视图传递请求上下文

确保序列化器能获取到当前请求对象:

from rest_framework_simplejwt.views import TokenObtainPairView

class TenantTokenObtainPairView(TokenObtainPairView):
    serializer_class = TenantTokenObtainPairSerializer

    def get_serializer_context(self):
        context = super().get_serializer_context()
        # 将request传入序列化器上下文
        context['request'] = self.request
        return context

4. 配置URL路由

将自定义视图替换默认的Token获取路由:

from django.urls import path
from .views import TenantTokenObtainPairView

urlpatterns = [
    path('api/token/', TenantTokenObtainPairView.as_view(), name='token_obtain_pair'),
    # 其他路由...
]

5. (可选)重写Token验证逻辑

如果认证服务器需要验证租户提交的Token,需替换验证时的密钥:

from rest_framework_simplejwt.authentication import JWTAuthentication
from rest_framework_simplejwt.tokens import Token
from rest_framework_simplejwt.exceptions import InvalidToken

class TenantJWTAuthentication(JWTAuthentication):
    def get_validated_token(self, raw_token):
        tenant_secret = get_tenant_secret(self.request)
        try:
            token = Token(raw_token)
            # 使用租户密钥验证Token签名
            token.verify(secret=tenant_secret)
            return token
        except Exception as e:
            raise InvalidToken(str(e))

在settings.py中配置自定义认证类:

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'your_app.path.to.TenantJWTAuthentication',
    ],
}

注意事项

  • 子域名提取逻辑需根据实际域名结构调整,避免解析错误。
  • 建议缓存租户密钥(如使用Django缓存框架),减少文件IO开销。
  • 确保租户环境文件的权限安全,避免密钥泄露。

内容的提问来源于stack exchange,提问作者Evren Bingøl

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 12:34:53