baseline: 批次A-D 成果 + membership 半成品(测试红)
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
"""开放平台认证与授权:
|
||||
APIKeyAuthentication 支持第三方系统通过 Header:
|
||||
X-API-Key: dh_xxxx.yyyy
|
||||
或
|
||||
Authorization: Api-Key dh_xxxx.yyyy
|
||||
安全访问开放接口。
|
||||
"""
|
||||
|
||||
from rest_framework import authentication, exceptions, permissions
|
||||
from django.utils import timezone
|
||||
from asgiref.sync import sync_to_async
|
||||
from .models import APIKey
|
||||
|
||||
|
||||
class APIKeyAuthentication(authentication.BaseAuthentication):
|
||||
"""基于 API Key 的第三方系统鉴权类。"""
|
||||
|
||||
def authenticate(self, request):
|
||||
auth_header = request.META.get("HTTP_AUTHORIZATION", "")
|
||||
api_key_header = request.META.get("HTTP_X_API_KEY", "")
|
||||
|
||||
raw_key = None
|
||||
if api_key_header:
|
||||
raw_key = api_key_header.strip()
|
||||
elif auth_header.startswith("Api-Key "):
|
||||
raw_key = auth_header[8:].strip()
|
||||
|
||||
if not raw_key:
|
||||
return None
|
||||
|
||||
if "." not in raw_key:
|
||||
raise exceptions.AuthenticationFailed("API Key 格式无效(必须包含前缀)")
|
||||
|
||||
prefix = raw_key.split(".")[0]
|
||||
key_obj = APIKey.objects.filter(prefix=prefix, is_active=True).select_related("tenant").first()
|
||||
if not key_obj:
|
||||
raise exceptions.AuthenticationFailed("API Key 不存在或已被禁用")
|
||||
|
||||
if key_obj.expires_at and key_obj.expires_at < timezone.now():
|
||||
raise exceptions.AuthenticationFailed("API Key 已过期")
|
||||
|
||||
if not key_obj.verify_key(raw_key):
|
||||
raise exceptions.AuthenticationFailed("API Key 签名无效")
|
||||
|
||||
# 记录调用时间并绑定 tenant
|
||||
key_obj.last_used_at = timezone.now()
|
||||
key_obj.save(update_fields=["last_used_at"])
|
||||
|
||||
# 注入租户上下文
|
||||
request.tenant_obj = key_obj.tenant
|
||||
if hasattr(request, "_request"):
|
||||
request._request.tenant_obj = key_obj.tenant
|
||||
|
||||
# 绑定系统用户
|
||||
user = key_obj.created_by
|
||||
if not user:
|
||||
from django.contrib.auth import get_user_model
|
||||
user = get_user_model().objects.filter(is_superuser=True).first()
|
||||
|
||||
return (user, key_obj)
|
||||
|
||||
|
||||
def require_scope(scope_name: str):
|
||||
"""用于视图权限检查的 Scope 装饰辅助类。"""
|
||||
|
||||
class ScopePermission(permissions.BasePermission):
|
||||
def has_permission(self, request, view):
|
||||
api_key = getattr(request, "auth", None)
|
||||
if not isinstance(api_key, APIKey):
|
||||
return False # 未携带有效 APIKey 则拒绝访问
|
||||
return scope_name in api_key.scopes or "*" in api_key.scopes
|
||||
|
||||
return ScopePermission
|
||||
Reference in New Issue
Block a user