You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
62 lines
2.1 KiB
62 lines
2.1 KiB
"""API 密钥认证中间件模块。
|
|
|
|
提供基于 API 密钥的流程访问认证功能。
|
|
主要用于外部系统通过 API Key 调用已发布的 AI 流程。
|
|
"""
|
|
import hashlib
|
|
from datetime import datetime
|
|
from fastapi import Request, HTTPException
|
|
from sqlalchemy import select
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
from models import FlowApiKey
|
|
from database import get_db
|
|
|
|
|
|
async def authenticate_api_key(request: Request) -> dict:
|
|
"""验证请求中的 API 密钥并返回关联的流程信息。
|
|
|
|
从 Authorization 请求头中提取 API Key,验证其有效性并更新最后使用时间。
|
|
API Key 必须以 "flow-" 开头,验证时对其 SHA-256 哈希值进行数据库匹配。
|
|
|
|
Args:
|
|
request: 当前 HTTP 请求对象。
|
|
|
|
Returns:
|
|
dict: 包含 flow_id、api_key_id 和 auth_type 的认证信息字典。
|
|
|
|
Raises:
|
|
HTTPException: 当缺少认证信息、API Key 格式无效或密钥不存在时抛出 401 异常。
|
|
"""
|
|
auth_header = request.headers.get("Authorization", "")
|
|
if not auth_header.startswith("Bearer "):
|
|
raise HTTPException(401, "缺少认证信息")
|
|
|
|
raw_key = auth_header[7:] # 提取 Bearer 后的密钥部分
|
|
if not raw_key.startswith("flow-"):
|
|
raise HTTPException(401, "无效的API Key格式")
|
|
|
|
key_hash = hashlib.sha256(raw_key.encode()).hexdigest() # 计算 SHA-256 哈希值
|
|
|
|
db_gen = get_db()
|
|
db: AsyncSession = await db_gen.__anext__()
|
|
try:
|
|
result = await db.execute(
|
|
select(FlowApiKey).where(FlowApiKey.key_hash == key_hash)
|
|
)
|
|
api_key = result.scalar_one_or_none()
|
|
if not api_key:
|
|
raise HTTPException(401, "API Key无效或已删除")
|
|
|
|
api_key.last_used_at = datetime.utcnow() # 更新最后使用时间
|
|
await db.flush()
|
|
|
|
return {
|
|
"flow_id": str(api_key.flow_id), # 关联的流程 ID
|
|
"api_key_id": str(api_key.id), # API Key 记录 ID
|
|
"auth_type": "api_key", # 认证类型标识
|
|
}
|
|
finally:
|
|
try:
|
|
await db_gen.__anext__()
|
|
except StopAsyncIteration:
|
|
pass
|
|
|