feat: Phase 1 - 安全中间件 + 运维工具箱
Some checks failed
CI / test (3.10) (push) Has been cancelled
CI / test (3.11) (push) Has been cancelled
CI / test (3.12) (push) Has been cancelled
CI / test (3.13) (push) Has been cancelled

新增 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:
2026-05-17 15:42:40 +08:00
parent e67d2d8ef6
commit 5e957096fa
12 changed files with 754 additions and 56 deletions

View File

@@ -0,0 +1,106 @@
"""JWT 认证 — 签发/验证/中间件"""
import json
import time
import hashlib
import hmac as hmac_mod
import base64
from typing import Optional
from oss.config import get_config
from oss.logger.logger import Log
class JWTError(Exception):
pass
class JWTAuth:
"""JWT 签发与验证HMAC-SHA256无外部依赖"""
ALGORITHM = "HS256"
HEADER = base64.b64encode(json.dumps({"alg": "HS256", "typ": "JWT"}).encode()).rstrip(b"=").decode()
def __init__(self, secret: str = None):
config = get_config()
self._secret = secret or config.get("JWT_SECRET", "")
if not self._secret:
self._secret = hashlib.sha256(config.get("API_KEY", "nebula-default-secret").encode()).hexdigest()
@staticmethod
def _b64url(data: bytes) -> str:
return base64.urlsafe_b64encode(data).rstrip(b"=").decode()
@staticmethod
def _unb64url(data: str) -> bytes:
padding = 4 - len(data) % 4
if padding != 4:
data += "=" * padding
return base64.urlsafe_b64decode(data)
def _sign(self, payload_b64: str) -> str:
msg = f"{JWTAuth.HEADER}.{payload_b64}".encode()
sig = hmac_mod.new(self._secret.encode(), msg, hashlib.sha256).digest()
return self._b64url(sig)
def issue(self, user_id: str, role: str = "admin", expire_hours: int = 24) -> str:
"""签发 JWT Token"""
payload = {
"sub": user_id,
"role": role,
"iat": int(time.time()),
"exp": int(time.time()) + expire_hours * 3600,
}
payload_b64 = self._b64url(json.dumps(payload).encode())
signature = self._sign(payload_b64)
return f"{JWTAuth.HEADER}.{payload_b64}.{signature}"
def verify(self, token: str) -> Optional[dict]:
"""验证 JWT Token返回 payload 或 None"""
try:
parts = token.split(".")
if len(parts) != 3:
return None
header_b64, payload_b64, sig_b64 = parts
# 验签
expected_sig = self._sign(payload_b64)
if not hmac_mod.compare_digest(expected_sig, sig_b64):
return None
# 解码 payload
payload = json.loads(self._unb64url(payload_b64))
# 检查过期
if payload.get("exp", 0) < time.time():
return None
return payload
except Exception:
return None
@staticmethod
def extract_token(auth_header: str) -> Optional[str]:
"""从 Authorization 头提取 Bearer Token"""
if not auth_header or not auth_header.startswith("Bearer "):
return None
return auth_header[7:]
# ── 快捷方法 ──
_auth_instance: Optional[JWTAuth] = None
def get_jwt_auth() -> JWTAuth:
global _auth_instance
if _auth_instance is None:
_auth_instance = JWTAuth()
return _auth_instance
def issue_token(user_id: str, role: str = "admin") -> str:
return get_jwt_auth().issue(user_id, role)
def verify_token(token: str) -> Optional[dict]:
return get_jwt_auth().verify(token)