From 340808e85ec698b105955b0d8fc431245fcf5b32 Mon Sep 17 00:00:00 2001 From: Jiang Date: Wed, 4 Mar 2026 16:06:41 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=AE=A1=E8=AE=A1=E4=B8=AD?= =?UTF-8?q?=E9=97=B4=E4=BB=B6=E6=8E=92=E9=99=A4=E8=B7=AF=E5=BE=84=E3=80=81?= =?UTF-8?q?=E7=94=A8=E6=88=B7=E6=8C=89=E7=94=A8=E6=88=B7=E5=90=8D=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2=E5=8A=9F=E8=83=BD=EF=BC=9B=E5=AE=8C=E5=96=84=E5=AE=A1?= =?UTF-8?q?=E8=AE=A1=E8=B5=84=E6=BA=90=E8=AE=B0=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/infra/audit/middleware.py | 48 +++++++++++++++++-- app/infra/repositories/metadata_repository.py | 6 +++ 2 files changed, 49 insertions(+), 5 deletions(-) diff --git a/app/infra/audit/middleware.py b/app/infra/audit/middleware.py index 7035ae9..556f5e0 100644 --- a/app/infra/audit/middleware.py +++ b/app/infra/audit/middleware.py @@ -55,6 +55,12 @@ class AuditMiddleware(BaseHTTPMiddleware): # 需要审计的HTTP方法 AUDIT_METHODS = ["POST", "PUT", "DELETE", "PATCH"] + EXCLUDED_PATHS = { + "/api/v1/meta/projects", + "/meta/projects", + "/api/v1/openproject/", + "/openproject/", + } async def dispatch(self, request: Request, call_next: Callable) -> Response: # 提取开始时间 @@ -84,6 +90,11 @@ class AuditMiddleware(BaseHTTPMiddleware): response = await call_next(request) # 3. 决定是否审计 + if request.url.path in self.EXCLUDED_PATHS: + process_time = time.time() - start_time + response.headers["X-Process-Time"] = str(process_time) + return response + # 检查方法 is_audit_method = request.method in self.AUDIT_METHODS # 检查路径 @@ -155,6 +166,7 @@ class AuditMiddleware(BaseHTTPMiddleware): token = auth_header.split(" ", 1)[1].strip() if not token: return None + sub = None try: key = ( settings.KEYCLOAK_PUBLIC_KEY.replace("\\n", "\n") @@ -166,17 +178,25 @@ class AuditMiddleware(BaseHTTPMiddleware): if settings.KEYCLOAK_PUBLIC_KEY else [settings.ALGORITHM] ) - payload = jwt.decode(token, key, algorithms=algorithms) + payload = jwt.decode( + token, + key, + algorithms=algorithms, + audience=settings.KEYCLOAK_AUDIENCE or None, + ) sub = payload.get("sub") if not sub: return None - keycloak_id = UUID(sub) - except (JWTError, ValueError): + except JWTError: return None async with SessionLocal() as session: repo = MetadataRepository(session) - user = await repo.get_user_by_keycloak_id(keycloak_id) + try: + keycloak_id = UUID(sub) + user = await repo.get_user_by_keycloak_id(keycloak_id) + except ValueError: + user = await repo.get_user_by_username(sub) if user and user.is_active: return user.id return None @@ -218,7 +238,25 @@ class AuditMiddleware(BaseHTTPMiddleware): if len(path_parts) >= 4: resource_type = path_parts[3].rstrip("s") # 移除复数s - if len(path_parts) >= 5 and path_parts[4].isdigit(): + if len(path_parts) >= 5 and path_parts[4]: resource_id = path_parts[4] + # 无路径ID时,尝试从查询参数提取业务ID + if not resource_id: + for key in ( + "id", + "resource_id", + "device_id", + "device_ids", + "element_id", + "user_id", + "project_id", + "network", + "name", + ): + value = request.query_params.get(key) + if value: + resource_id = value + break + return resource_type, resource_id diff --git a/app/infra/repositories/metadata_repository.py b/app/infra/repositories/metadata_repository.py index d8fbeee..b9c47b7 100644 --- a/app/infra/repositories/metadata_repository.py +++ b/app/infra/repositories/metadata_repository.py @@ -77,6 +77,12 @@ class MetadataRepository: ) return result.scalar_one_or_none() + async def get_user_by_username(self, username: str) -> Optional[models.User]: + result = await self.session.execute( + select(models.User).where(models.User.username == username) + ) + return result.scalar_one_or_none() + async def get_project_by_id(self, project_id: UUID) -> Optional[models.Project]: result = await self.session.execute( select(models.Project).where(models.Project.id == project_id)