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:
106
oss/core/security/jwt_auth.py
Normal file
106
oss/core/security/jwt_auth.py
Normal 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)
|
||||
Reference in New Issue
Block a user