From 46c3a197be2bd16289a22737abbdfabdf041d347 Mon Sep 17 00:00:00 2001 From: qing <1776177662@qq.com> Date: Thu, 30 Jul 2026 09:08:33 +0800 Subject: [PATCH] =?UTF-8?q?refactor(auth):=20=E7=AE=A1=E7=90=86=E5=91=98?= =?UTF-8?q?=E5=88=A4=E5=AE=9A=E4=B8=8A=E7=A7=BB=E8=87=B3=20gateway?= =?UTF-8?q?=EF=BC=8C=E7=BB=8F=20Token=20payload=20=E4=B8=8B=E5=8F=91=20is?= =?UTF-8?q?=5Fadmin?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gateway 登录签发 Token 时,账号命中 [Admin] users 白名单即在 payload 写入 is_admin=true(随 RSA 签名保护);dnfs 移除本地 ADMIN_USERS 硬编码, delete_note/update_note 豁免判断改读 payload 的 is_admin 字段。 旧 Token 无此字段按非管理员处理(安全方向)。 --- dnfs/dnfs.py | 9 +++++---- gateway/example.ini | 6 ++++++ gateway/src/main.cpp | 10 +++++++++- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/dnfs/dnfs.py b/dnfs/dnfs.py index cf10c37..6fdd957 100755 --- a/dnfs/dnfs.py +++ b/dnfs/dnfs.py @@ -37,8 +37,9 @@ from pymsg import MsgClient # noqa: E402 _DBV2_ADDR = '127.0.0.1' _DBV2_PORT = 8005 -# 管理员账号:删除/更新节点时豁免所有权校验(保留旧的 'admin' 以兼容历史数据) -ADMIN_USERS = ('admin@qsont.me', 'qing@qsont.me') +# 管理员判定已上移至 gateway:登录时命中 [Admin] users 白名单即在 Token payload +# 写入 is_admin=true,本服务通过 info.get('is_admin') 读取,不再本地维护名单。 +# 旧 Token 无此字段,按非管理员处理(安全方向)。 def _call_dbv2(payload: dict, timeout: int = 10) -> dict: """通过 pymsg 短连接调用 dbv2 数据库服务。 @@ -326,7 +327,7 @@ def delete_note(): data = _call_dbv2(select_payload) notes = data['data'] - if info['user'] not in ADMIN_USERS: + if not info.get('is_admin'): if not len(notes) == 1 or not notes[0]['owner'] == info['user']: logs.warning('dnfs', f'delete_note 所有权拒绝 id={note_id}', extra={'user': info['user']}) return jsonify({"error": 'It\'s not your note'}), 403 @@ -401,7 +402,7 @@ def update_note(): data = _call_dbv2(select_payload) notes = data['data'] - if info['user'] not in ADMIN_USERS: + if not info.get('is_admin'): if not len(notes) == 1 or not notes[0]['owner'] == info['user']: logs.warning('dnfs', f'update_note 所有权拒绝 id={id}', extra={'user': info['user']}) return jsonify({"error": 'It\'s not your note'}), 403 diff --git a/gateway/example.ini b/gateway/example.ini index f6fbd24..f26f27d 100644 --- a/gateway/example.ini +++ b/gateway/example.ini @@ -15,6 +15,12 @@ opened = http://127.0.0.1:9200;http://127.0.0.1:8006; # Token 有效期(秒),默认 3600 = 1 小时 keep_time = 3600 +[Admin] +# 管理员账号白名单(分号分隔)。登录时命中即在 Token payload 写入 is_admin=true, +# 下游服务(dnfs 等)凭该字段豁免所有权校验,无需各自维护名单。 +# 修改后需重启 gateway 生效;旧 Token 无此字段,按非管理员处理。 +users = qing@qsont.me + [CORS] # 允许跨域的 Origin 白名单(分号分隔) # 仅命中的 Origin 才会回填 Access-Control-Allow-Origin;缺省为空(不回填) diff --git a/gateway/src/main.cpp b/gateway/src/main.cpp index 391deb7..eae3dcf 100644 --- a/gateway/src/main.cpp +++ b/gateway/src/main.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -325,6 +326,7 @@ static std::vector _OPEN_URL; static std::string _DB_PATH; static int _TOKEN_KEEP_TIME = 3600; // Token 有效期(秒),可由 [Token] keep_time 配置 static std::vector _CORS_ORIGINS; // CORS 允许的 Origin 白名单,可由 [CORS] allowed_origins 配置 +static std::vector _ADMIN_USERS; // 管理员账号白名单,可由 [Admin] users 配置(分号分隔),登录时写入 Token payload 的 is_admin /* instance items */ static std::unique_ptr _SERVE; @@ -498,6 +500,7 @@ int main(int argc, char **argv) { _DB_PATH = cf.get("Database.path", ""); _TOKEN_KEEP_TIME = cf.get("Token.keep_time", 3600); _CORS_ORIGINS = split(cf.get("CORS.allowed_origins", ""), ';'); + _ADMIN_USERS = split(cf.get("Admin.users", ""), ';'); } @@ -867,10 +870,15 @@ http::response handleLogin( /* 生成令牌 */ auto _PRIV = MyRSA::Private_Key::from_pem(_KEYGEN->get_private_key_pem()); + /* 管理员判定:账号命中 [Admin] users 白名单即在 payload 中签发 is_admin, + * 下游服务(dnfs 等)直接读取该字段豁免所有权校验,无需各自维护名单 */ + bool is_admin = std::find(_ADMIN_USERS.begin(), _ADMIN_USERS.end(), username) != _ADMIN_USERS.end(); + json::value jv_t = { {"user", username}, {"created_at", LocalTime::getCurrent()}, - {"keep_time", _TOKEN_KEEP_TIME} + {"keep_time", _TOKEN_KEEP_TIME}, + {"is_admin", is_admin} }; std::string data = json::serialize(jv_t); std::string auth = Base64::base64_encode(data);