- msync 全部实现迁入 gvsdsdk(models/registry/ogm/transaction/auth + Django 集成层 middleware/sio/views/serializers/urls/admin/apps/migrations) - 新增 gvsdsdk/secrets.py(secrets.ini 读取)与 serializer_base.py (PascalCase DRF 序列化器基类) - msync 0001 迁移改用 gvsdsdk.models.UUIDField 并与模型 deep_deconstruct 完全对齐(消除 makemigrations 漂移),dependencies 置空 - msync apps.py 移除 gvsds.app_config 软依赖,域名仅经环境变量/settings 解析 - 验证:屏蔽 gvsds 包后 50 个 gvsdsdk 子模块独立导入通过
155 lines
5.0 KiB
Python
155 lines
5.0 KiB
Python
"""模块:服务请求验证中间件与权限装饰器
|
|
用于验证来自其他微服务的请求,提取服务身份信息。
|
|
在 Django 视图中使用 @require_service_scope 装饰器保护端点。"""
|
|
|
|
import functools
|
|
import logging
|
|
from django.http import JsonResponse
|
|
from rest_framework.response import Response
|
|
from gvsdsdk.msync.auth import ValidateAccessToken
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class ServiceAuthMiddleware:
|
|
"""服务认证中间件——从请求中提取并验证服务令牌
|
|
|
|
将验证结果附加到 request 对象:
|
|
- request.service_name: 发起请求的服务名称
|
|
- request.service_scope: 服务的权限范围列表
|
|
- request.service_token: 原始令牌字符串
|
|
"""
|
|
|
|
SERVICE_AUTH_HEADER = 'Authorization'
|
|
SERVICE_AUTH_PREFIX = 'Service'
|
|
|
|
def __init__(self, get_response):
|
|
self.get_response = get_response
|
|
|
|
def __call__(self, request):
|
|
request.service_name = None
|
|
request.service_scope = []
|
|
request.service_token = None
|
|
|
|
auth_header = request.META.get('HTTP_AUTHORIZATION', '')
|
|
if auth_header.startswith(f'{self.SERVICE_AUTH_PREFIX} '):
|
|
token = auth_header[len(self.SERVICE_AUTH_PREFIX) + 1:]
|
|
payload = ValidateAccessToken(token)
|
|
if payload:
|
|
request.service_name = payload.get('sub')
|
|
request.service_scope = payload.get('scope', [])
|
|
request.service_token = token
|
|
|
|
response = self.get_response(request)
|
|
return response
|
|
|
|
|
|
def require_service_scope(*required_scopes):
|
|
"""视图装饰器——要求请求来自具有指定权限范围的已认证服务
|
|
|
|
用法:
|
|
@require_service_scope('ogm:read')
|
|
def my_view(request):
|
|
# request.service_name 已设置
|
|
...
|
|
"""
|
|
|
|
def decorator(view_func):
|
|
@functools.wraps(view_func)
|
|
def wrapper(request, *args, **kwargs):
|
|
if not request.service_name:
|
|
return JsonResponse(
|
|
{'code': 401, 'msg': '需要服务认证', 'data': None},
|
|
status=401,
|
|
)
|
|
|
|
if '*' not in request.service_scope:
|
|
for scope in required_scopes:
|
|
if scope not in request.service_scope:
|
|
return JsonResponse(
|
|
{'code': 403, 'msg': f'缺少权限: {scope}', 'data': None},
|
|
status=403,
|
|
)
|
|
|
|
return view_func(request, *args, **kwargs)
|
|
|
|
return wrapper
|
|
|
|
return decorator
|
|
|
|
|
|
def require_service_scope_drq(*required_scopes):
|
|
"""DRF 视图装饰器——返回 DRF Response 而非 Django JsonResponse"""
|
|
|
|
def decorator(view_func):
|
|
@functools.wraps(view_func)
|
|
def wrapper(self, request, *args, **kwargs):
|
|
if not request.service_name:
|
|
return Response(
|
|
{'code': 401, 'msg': '需要服务认证', 'data': None},
|
|
status=401,
|
|
)
|
|
|
|
if '*' not in request.service_scope:
|
|
for scope in required_scopes:
|
|
if scope not in request.service_scope:
|
|
return Response(
|
|
{'code': 403, 'msg': f'缺少权限: {scope}', 'data': None},
|
|
status=403,
|
|
)
|
|
|
|
return view_func(self, request, *args, **kwargs)
|
|
|
|
return wrapper
|
|
|
|
return decorator
|
|
|
|
|
|
class ServicePermission:
|
|
"""DRF 权限类——用于 ViewSet 的 permission_classes"""
|
|
|
|
def __init__(self, *required_scopes):
|
|
self.required_scopes = required_scopes
|
|
|
|
def has_permission(self, request, view):
|
|
if not request.service_name:
|
|
return False
|
|
if '*' in request.service_scope:
|
|
return True
|
|
return all(s in request.service_scope for s in self.required_scopes)
|
|
|
|
|
|
class ServiceTokenAuthentication:
|
|
"""DRF 认证类——从请求头中提取并验证服务令牌
|
|
|
|
配合 ServiceAuthMiddleware 使用,中间件已将验证结果附加到 request 对象。
|
|
此类使 DRF 的 request.user 和 request.auth 可用于权限检查。
|
|
"""
|
|
|
|
def authenticate(self, request):
|
|
service_name = getattr(request, 'service_name', None)
|
|
if not service_name:
|
|
return None
|
|
# 返回 (user, auth) 元组,使 DRF 认为请求已认证
|
|
# service_name 作为 user 标识,service_token 作为 auth
|
|
return (ServicePrincipal(request), request.service_token)
|
|
|
|
def authenticate_header(self, request):
|
|
return 'Service'
|
|
|
|
|
|
class ServicePrincipal:
|
|
"""服务主体——作为 DRF 认证后的 request.user"""
|
|
|
|
def __init__(self, request):
|
|
self.service_name = request.service_name
|
|
self.service_scope = request.service_scope
|
|
self.service_token = request.service_token
|
|
|
|
@property
|
|
def is_authenticated(self):
|
|
return True
|
|
|
|
def __str__(self):
|
|
return f'Service:{self.service_name}'
|