feat: Phase 1 - 安全中间件 + 运维工具箱
新增 oss/core/security/ 模块(852行): - jwt_auth.py: JWT签发/验证(HMAC-SHA256,零外部依赖) - csrf.py: CSRF Token生成与校验 - input_validator.py: JSON Schema校验+类型强制 - tls.py: 自签名证书生成+SSL上下文 新增 oss/core/ops/ 模块: - health.py: 增强版/health端点(CPU/内存/磁盘/运行时间) - metrics.py: Prometheus兼容/metrics端点 对接改造: - engine.py: 导出新模块 - manager.py: 注册/api/login /health /metrics路由 - middleware.py: CSRF+InputValidation中间件 - config.py: JWT_SECRET/CSRF_SECRET等配置项 - security.py→security/__init__.py: 合并插件沙箱与HTTP安全
This commit is contained in:
@@ -41,7 +41,7 @@ class CorsMiddleware(Middleware):
|
||||
|
||||
|
||||
class AuthMiddleware(Middleware):
|
||||
"""鉴权中间件 - Bearer Token 认证"""
|
||||
"""鉴权中间件 - JWT + API_KEY 双模式认证"""
|
||||
|
||||
@staticmethod
|
||||
def _get_public_paths() -> set:
|
||||
@@ -50,34 +50,47 @@ class AuthMiddleware(Middleware):
|
||||
configured = config.get("PUBLIC_PATHS")
|
||||
if configured and isinstance(configured, list):
|
||||
return set(configured)
|
||||
return {"/health", "/favicon.ico", "/api/status", "/api/health"}
|
||||
return {"/health", "/favicon.ico", "/api/status", "/api/health", "/api/login", "/metrics"}
|
||||
|
||||
def process(self, ctx: dict, next_fn: Callable) -> Optional[Response]:
|
||||
config = get_config()
|
||||
api_key = config.get("API_KEY")
|
||||
|
||||
if not api_key:
|
||||
return next_fn()
|
||||
api_key = config.get("API_KEY", "")
|
||||
|
||||
public_paths = self._get_public_paths()
|
||||
req = ctx.get("request")
|
||||
if req and req.path in public_paths:
|
||||
return next_fn()
|
||||
|
||||
if req and req.method == "OPTIONS":
|
||||
return next_fn()
|
||||
if not api_key:
|
||||
# 无 API_KEY 时尝试 JWT 鉴权
|
||||
auth_header = req.headers.get("Authorization", "") if req else ""
|
||||
token = auth_header.removeprefix("Bearer ").strip()
|
||||
if token:
|
||||
from oss.core.security.jwt_auth import verify_token
|
||||
payload = verify_token(token)
|
||||
if payload:
|
||||
ctx["user"] = payload
|
||||
return next_fn()
|
||||
return Response(
|
||||
status=401,
|
||||
body=json.dumps({"error": "Unauthorized", "message": "Token 无效或已过期"}),
|
||||
headers={"Content-Type": "application/json"},
|
||||
)
|
||||
return next_fn()
|
||||
|
||||
# API_KEY 模式
|
||||
auth_header = req.headers.get("Authorization", "") if req else ""
|
||||
token = auth_header.removeprefix("Bearer ").strip()
|
||||
if token == api_key and token:
|
||||
return next_fn()
|
||||
|
||||
if token != api_key or not token:
|
||||
Log.warn("Core", f"鉴权失败: {req.method} {req.path}" if req else "鉴权失败")
|
||||
return Response(
|
||||
status=401,
|
||||
body=json.dumps({"error": "Unauthorized", "message": "需要有效的 API Key"}),
|
||||
headers={"Content-Type": "application/json"},
|
||||
)
|
||||
return next_fn()
|
||||
Log.warn("Core", f"鉴权失败: {req.method} {req.path}" if req else "鉴权失败")
|
||||
return Response(
|
||||
status=401,
|
||||
body=json.dumps({"error": "Unauthorized", "message": "需要有效的认证凭据"}),
|
||||
headers={"Content-Type": "application/json"},
|
||||
)
|
||||
|
||||
|
||||
class LoggerMiddleware(Middleware):
|
||||
@@ -91,6 +104,51 @@ class LoggerMiddleware(Middleware):
|
||||
return next_fn()
|
||||
|
||||
|
||||
class CSRFMiddleware(Middleware):
|
||||
"""CSRF 防护中间件"""
|
||||
|
||||
def process(self, ctx: dict, next_fn: Callable) -> Optional[Response]:
|
||||
config = get_config()
|
||||
if not config.get("CSRF_ENABLED", True):
|
||||
return next_fn()
|
||||
|
||||
req = ctx.get("request")
|
||||
if not req or CSRFProtection.is_safe_method(req.method):
|
||||
return next_fn()
|
||||
|
||||
# 从 Header 或 Body 中获取 CSRF Token
|
||||
token = req.headers.get("X-CSRF-Token", "")
|
||||
session_id = req.headers.get("X-Session-Id", "")
|
||||
|
||||
if not token or not session_id:
|
||||
return Response(
|
||||
status=403,
|
||||
body=json.dumps({"error": "Forbidden", "message": "缺少 CSRF Token"}),
|
||||
headers={"Content-Type": "application/json"},
|
||||
)
|
||||
|
||||
from oss.core.security.csrf import CSRFProtection
|
||||
csrf = CSRFProtection()
|
||||
if not csrf.verify_token(session_id, token):
|
||||
return Response(
|
||||
status=403,
|
||||
body=json.dumps({"error": "Forbidden", "message": "CSRF Token 无效"}),
|
||||
headers={"Content-Type": "application/json"},
|
||||
)
|
||||
|
||||
return next_fn()
|
||||
|
||||
|
||||
class InputValidationMiddleware(Middleware):
|
||||
"""输入验证中间件"""
|
||||
|
||||
def process(self, ctx: dict, next_fn: Callable) -> Optional[Response]:
|
||||
config = get_config()
|
||||
if not config.get("INPUT_VALIDATION_ENABLED", True):
|
||||
return next_fn()
|
||||
return next_fn() # 具体 schema 校验在路由 handler 中按需调用
|
||||
|
||||
|
||||
class MiddlewareChain:
|
||||
"""中间件链"""
|
||||
|
||||
@@ -98,6 +156,8 @@ class MiddlewareChain:
|
||||
self.middlewares: list[Middleware] = []
|
||||
self.add(CorsMiddleware())
|
||||
self.add(AuthMiddleware())
|
||||
self.add(CSRFMiddleware())
|
||||
self.add(InputValidationMiddleware())
|
||||
self.add(LoggerMiddleware())
|
||||
self.add(RateLimitMiddleware())
|
||||
|
||||
|
||||
Reference in New Issue
Block a user