根因:元景迭代中为通过 QC 门禁反复叠加了 24 个自定义中间件
(auth_gate_strict / session_gate / login_param_source / route_guard /
entry_semantics / http_semantics / notfound / get_only_405 / ...),
导致标准登录链路被破坏:
- auth_gate_strict 手写 Fernet 解密 + 用 aiohttp_session 的会话创建时间
created 按 1 天判过期 → 所有受保护端点带 cookie 仍 401
- route_guard 的 _has_business_params 用 await request.read() 消费 POST body
→ up_login 收到 params_kw={} → password_encode(None) 崩 500
改动:
- app/scense.py 回归 57 行标准入口:ServerEnv + load_appbase/load_rbac/
load_pybricks + 业务模块 load_*,零自定义中间件
- 24 个自造中间件归档到 app/_archive_disabled_20261028_151505/(可回滚)
- /healthz 改用 ahserver 标准 FunctionProcessor(RegisterFunction 注册 +
conf.website.startswiths 映射),不再用中间件拦截
实测闭环(127.0.0.1:9380 + https://gbak.opencomputing.cn):
healthz 200 | 匿名 401 | admin 登录 200+cookie | 带cookie 200 |
伪造cookie 401 | 注册 200+自动登录 | 新用户登录 200+cookie |
新用户访问 403(已认证无权限,RBAC 正确行为)| 登出 200 |
错误密码 200 业务信封(不崩 500)
register/login/logout/session 全部由 rbac 模块 up_login.dspy /
register.dspy / logout.dspy + ahserver session 中间件提供。
114 lines
3.5 KiB
Python
114 lines
3.5 KiB
Python
"""唤景平台世界模式(scense)——应用唯一入口。
|
||
|
||
一个应用 = 一个入口 = 一个端口。
|
||
标准 ahserver + rbac 提供注册/登录/会话管理,应用不自行实现鉴权中间件。
|
||
"""
|
||
from ahserver.webapp import webapp
|
||
from ahserver.serverenv import ServerEnv
|
||
|
||
import logging
|
||
import os
|
||
import sys
|
||
|
||
import bricks_for_python # registers bui processor
|
||
from bricks_for_python.init import load_pybricks # registers UiWindow etc.
|
||
from appbase.init import load_appbase
|
||
from rbac.init import load_rbac
|
||
|
||
log = logging.getLogger("scense.entry")
|
||
|
||
_APP_DIR = os.path.dirname(os.path.abspath(__file__))
|
||
_DEPLOY_DIR = os.path.dirname(_APP_DIR)
|
||
for _p in (_APP_DIR, _DEPLOY_DIR):
|
||
while _p in sys.path:
|
||
sys.path.remove(_p)
|
||
sys.path.insert(0, _DEPLOY_DIR)
|
||
sys.path.append(_APP_DIR)
|
||
|
||
|
||
def get_module_dbname(m):
|
||
"""返回模块 m 对应的数据库名(本项目单库:全部落 scense)。"""
|
||
return "scense"
|
||
|
||
|
||
def password_encode(s):
|
||
if s is None:
|
||
return ""
|
||
from ahserver.globalEnv import password_encode as _orig
|
||
return _orig(s)
|
||
|
||
|
||
def _healthz(request, params_kw, *args, **kw):
|
||
"""/healthz 健康探针(标准 ahserver FunctionProcessor 注册,config.website.startswiths)。"""
|
||
from aiohttp import web
|
||
return web.json_response({
|
||
"ok": True,
|
||
"status": 200,
|
||
"app": "scense",
|
||
"entry": "app/scense.py",
|
||
})
|
||
|
||
|
||
def _drive(_aw):
|
||
"""驱动异步加载器(entity.init.load_entity / world_snapshot.init.load_world_snapshot 是 async def)。"""
|
||
if not hasattr(_aw, "__await__"):
|
||
return _aw
|
||
import asyncio
|
||
try:
|
||
asyncio.get_running_loop()
|
||
except RuntimeError:
|
||
return asyncio.run(_aw)
|
||
import concurrent.futures
|
||
with concurrent.futures.ThreadPoolExecutor(max_workers=1) as _ex:
|
||
return _ex.submit(lambda: asyncio.run(_aw)).result()
|
||
|
||
|
||
def init():
|
||
# `python app/scense.py` 会把脚本目录置于 sys.path[0],遮蔽同名业务模块包
|
||
_script_dir = os.path.join(_DEPLOY_DIR, "app")
|
||
while _script_dir in sys.path:
|
||
sys.path.remove(_script_dir)
|
||
sys.path.append(_script_dir)
|
||
if _DEPLOY_DIR not in sys.path:
|
||
sys.path.insert(0, _DEPLOY_DIR)
|
||
|
||
env = ServerEnv() # MUST be before load_rbac / load_appbase
|
||
env.get_module_dbname = get_module_dbname
|
||
env.password_encode = password_encode
|
||
|
||
load_appbase()
|
||
load_rbac()
|
||
load_pybricks()
|
||
|
||
# ── 业务模块导入(一个入口一个端口,所有模块挂在这里)──
|
||
from world.init import load_world
|
||
from scene.init import load_scene
|
||
from entity.init import load_entity
|
||
from script_engine.init import load_script_engine
|
||
from world_snapshot.init import load_world_snapshot
|
||
from world_sync.init import load_world_sync
|
||
from scense_demo.init import load_demo
|
||
from scense.init import load_scense
|
||
from scense_game.init import load_scense_game
|
||
from scense_drag.init import load_drag
|
||
from runtime.init import load_runtime
|
||
|
||
load_world()
|
||
load_scene()
|
||
_drive(load_entity())
|
||
load_script_engine()
|
||
_drive(load_world_snapshot())
|
||
load_world_sync()
|
||
load_demo(env)
|
||
load_scense(env)
|
||
load_scense_game(env)
|
||
load_drag()
|
||
load_runtime(env)
|
||
|
||
# ── 标准函数处理器注册(配合 conf/config.json website.startswiths)──
|
||
from appPublic.registerfunction import RegisterFunction
|
||
RegisterFunction().register("healthz", _healthz)
|
||
|
||
|
||
if __name__ == "__main__":
|
||
webapp(init) |