You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Django多应用架构下跨独立代码库共享自定义令牌验证与权限类的方案咨询

Django多应用架构下跨独立代码库共享自定义令牌验证与权限类的方案咨询

我理解你的核心问题是:多个独立的Django项目(App1/2/3)需要复用Auth服务中的自定义令牌验证逻辑,但这些项目无法直接访问Auth服务的AuthToken模型和本地验证代码。下面是几种生产环境中常用的解决方案,从可维护性到快速实现各有侧重,你可以根据团队规模和项目阶段选择:


方案1:封装共享认证库(推荐长期维护)

这是最规范的做法,将通用的认证、权限逻辑打包成独立的Python包,让所有App通过pip安装复用,后续迭代只需更新包版本即可。

步骤:

  1. 创建共享认证包
    新建一个Python包(比如django-auth-shared),目录结构如下:
    django-auth-shared/
    ├── setup.py
    ├── auth_shared/
    │   ├── __init__.py
    │   ├── authentication.py  # 存放AuthTokenAuthentication
    │   ├── permissions.py     # 共享权限类
    │   └── conf.py            # 可配置的Auth服务地址等
    
  2. 修改认证类为远程调用模式
    不再直接操作AuthToken模型,而是通过HTTP请求调用Auth服务的验证接口。示例代码:
    # auth_shared/authentication.py
    import requests
    from rest_framework.authentication import BaseAuthentication
    from rest_framework.exceptions import AuthenticationFailed
    from django.conf import settings
    
    class AuthTokenAuthentication(BaseAuthentication):
        def authenticate(self, request):
            auth_header = request.META.get('HTTP_AUTHORIZATION')
            if not auth_header or not auth_header.startswith('Bearer '):
                return None
    
            token = auth_header.split(' ')[1]
            # 调用Auth服务的验证接口
            validate_url = getattr(settings, 'AUTH_SERVICE_VALIDATE_URL', 'https://auth.example.com/api/validate-token/')
            try:
                response = requests.post(
                    validate_url,
                    json={'token': token},
                    timeout=5,
                    verify=True  # 确保HTTPS证书验证
                )
                response.raise_for_status()
                data = response.json()
            except requests.exceptions.RequestException as e:
                raise AuthenticationFailed(f'认证服务连接失败: {str(e)}')
    
            if not data.get('is_valid'):
                raise AuthenticationFailed(data.get('error', '无效令牌'))
    
            # 构造用户对象:可关联本地User模型,或用自定义临时用户
            from django.contrib.auth.models import User
            try:
                # 假设Auth服务返回user_id,关联本地用户
                user = User.objects.get(id=data['user_id'])
                # 附加Auth服务返回的角色/权限信息
                user.roles = data.get('roles', [])
            except User.DoesNotExist:
                # 无本地用户时可抛出异常,或创建临时匿名用户
                raise AuthenticationFailed('本地系统未找到该用户')
    
            return (user, token)
    
  3. 在Auth服务实现验证接口
    在Auth服务中编写视图,接收令牌并返回验证结果:
    # Auth服务的views.py
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework.permissions import AllowAny
    from .models import AuthToken
    from django.utils import timezone
    
    class ValidateTokenView(APIView):
        permission_classes = [AllowAny]
    
        def post(self, request):
            token = request.data.get('token')
            if not token:
                return Response({'is_valid': False, 'error': '令牌缺失'}, status=400)
    
            try:
                auth_token = AuthToken.objects.get(token=token)
            except AuthToken.DoesNotExist:
                return Response({'is_valid': False, 'error': '无效令牌'}, status=401)
    
            # 验证令牌状态
            if auth_token.is_revoked:
                return Response({'is_valid': False, 'error': '令牌已吊销'}, status=401)
            if auth_token.expires_at and auth_token.expires_at < timezone.now():
                return Response({'is_valid': False, 'error': '令牌已过期'}, status=401)
    
            # 返回用户ID、角色等必要信息
            return Response({
                'is_valid': True,
                'user_id': auth_token.user.id,
                'platform_id': auth_token.platform.id,
                'roles': [role.name for role in auth_token.user.roles.all()]
            })
    
  4. 在App1/2/3中使用
    • 安装共享包:pip install git+https://your-repo/django-auth-shared.git(或发布到PyPI)
    • 配置settings.py:
      # settings.py
      REST_FRAMEWORK = {
          'DEFAULT_AUTHENTICATION_CLASSES': [
              'auth_shared.authentication.AuthTokenAuthentication',
          ],
          'DEFAULT_PERMISSION_CLASSES': [
              'auth_shared.permissions.IsPlatformAdmin',
          ],
      }
      # 配置Auth服务验证地址
      AUTH_SERVICE_VALIDATE_URL = 'https://auth.yourdomain.com/api/validate-token/'
      

优缺点:

  • ✅ 可维护性高:修改认证逻辑只需更新共享包,所有App同步生效
  • ✅ 代码复用彻底:权限类、工具函数可统一管理
  • ❌ 初期有包搭建成本:需要掌握Python包发布/安装流程

方案2:直接调用Auth服务验证接口(快速实现)

如果团队暂时不想维护独立包,可以让每个App直接调用Auth服务的验证接口,认证类可通过Git子模块共享,避免重复复制。

步骤:

  1. 同方案1,在Auth服务实现ValidateTokenView
  2. 共享认证类到Git仓库
    AuthTokenAuthentication.py和权限类放在单独的Git仓库,每个App通过Git子模块引用:
    # 在App1中添加子模块
    git submodule add https://your-repo/auth-shared-code.git
    
  3. 配置每个App的settings.py
    REST_FRAMEWORK = {
        'DEFAULT_AUTHENTICATION_CLASSES': [
            'auth-shared-code.authentication.AuthTokenAuthentication',
        ],
    }
    # 配置Auth服务地址
    AUTH_SERVICE_VALIDATE_URL = 'https://auth.yourdomain.com/api/validate-token/'
    

优缺点:

  • ✅ 快速上手:无需打包,直接写代码调用API
  • ❌ 维护成本高:修改认证逻辑需要同步更新所有App的子模块
  • ❌ 依赖Git操作:需要团队成员熟悉子模块使用

方案3:替换为JWT令牌(无状态验证)

如果业务场景允许,用JWT替代自定义AuthToken模型,App1/2/3无需调用Auth服务,直接本地解析JWT验证签名、过期时间,实现无状态认证。

步骤:

  1. 在Auth服务生成JWT令牌
    使用djangorestframework-simplejwt库快速实现:
    # Auth服务的登录视图
    from rest_framework_simplejwt.tokens import RefreshToken
    from rest_framework.response import Response
    
    class LoginView(APIView):
        def post(self, request):
            user = authenticate(request, username=request.data['username'], password=request.data['password'])
            if user:
                refresh = RefreshToken.for_user(user)
                # 添加自定义字段(如平台ID)
                refresh['platform_id'] = request.data.get('platform_id')
                return Response({
                    'refresh': str(refresh),
                    'access': str(refresh.access_token),
                })
    
  2. 在App1/2/3中配置JWT认证
    安装库并配置settings.py:
    # App1的settings.py
    REST_FRAMEWORK = {
        'DEFAULT_AUTHENTICATION_CLASSES': [
            'rest_framework_simplejwt.authentication.JWTAuthentication',
        ],
    }
    
    SIMPLE_JWT = {
        'SIGNING_KEY': 'your-shared-secret-key',  # 与Auth服务使用同一密钥
        'AUTH_HEADER_TYPES': ('Bearer',),
        'ACCESS_TOKEN_LIFETIME': timedelta(minutes=15),  # 短有效期降低泄露风险
    }
    
  3. 处理令牌吊销(可选)
    JWT无法主动吊销,可通过以下方式弥补:
    • 用短有效期的access token + 刷新令牌
    • Auth服务维护JWT黑名单,App验证时调用黑名单接口

优缺点:

  • ✅ 无状态:App1/2/3无需调用Auth服务,验证速度快
  • ✅ 代码简洁:使用成熟库,无需自定义认证类
  • ❌ 无法实时吊销令牌(除非结合黑名单)
  • ❌ 密钥管理风险:所有App共享签名密钥,泄露会导致安全问题

权限类共享建议

不管用哪种方案,共享权限类的思路一致:

  1. 放在共享包中(方案1)
  2. 放在共享Git仓库(方案2)
  3. 权限基于角色时,让Auth服务返回角色信息,App端权限类根据角色判断:
    # 共享权限类示例
    from rest_framework.permissions import BasePermission
    
    class IsPlatformAdmin(BasePermission):
        def has_permission(self, request, view):
            return 'platform_admin' in getattr(request.user, 'roles', [])
    

最终建议

  • 大型长期项目:优先选择方案1(共享包),可维护性最高
  • 小型快速验证项目:选择方案2(直接调用API),快速实现
  • 无需实时吊销令牌的场景:选择方案3(JWT),无状态验证最简洁

额外注意事项:

  • 所有通信使用HTTPS,防止令牌被窃听
  • 给Auth服务的验证接口添加速率限制,防止暴力请求
  • 定期轮换JWT签名密钥(方案3)或共享包的依赖版本(方案1)

火山引擎 最新活动