scense/app/scense.py
ymq 702f27407b refactor(auth): 移除全部自造鉴权/会话/语义中间件,回归标准 ahserver+rbac
根因:元景迭代中为通过 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 中间件提供。
2026-10-28 15:19:43 +08:00

114 lines
3.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""唤景平台世界模式(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)