From e7a287742247ee573daf3d2545f96f3a8edfdb47 Mon Sep 17 00:00:00 2001 From: qing <1776177662@qq.com> Date: Sat, 1 Aug 2026 13:42:59 +0800 Subject: [PATCH] feat(dnfs): delegate Token verification to gateway /whoami Replace the insecure local base64 decode (with FIXME comment about missing signature verification) with proper remote RSA signature verification through gateway's /whoami endpoint: - Add [gateway] config section (url, timeout) to dnfs.ini with defaults. - Support GATEWAY_URL environment variable override for Docker. - decode_token() now POSTs to gateway /whoami with Authorization: Bearer ; gateway verifies RSA signature and expiry, returns login/user/ is_admin. Network/gateway errors fail-closed with 503. - Add @_handle_exp decorators to delete_note() and update_note() which were missing exception handling wrappers (consistent with other routes). - docker-compose.yml: add GATEWAY_URL, CONFS_HOST, CONFS_PORT env vars plus host.docker.internal extra_hosts so dnfs container can reach host-side gateway and confs services. --- confs/configs/dnfs.ini | 4 +++ dnfs/dnfs.py | 60 ++++++++++++++++++++++++++++++++++-------- docker-compose.yml | 5 ++++ 3 files changed, 58 insertions(+), 11 deletions(-) diff --git a/confs/configs/dnfs.ini b/confs/configs/dnfs.ini index fd47bbd..f327890 100644 --- a/confs/configs/dnfs.ini +++ b/confs/configs/dnfs.ini @@ -10,3 +10,7 @@ timeout = 10 [ai_review] url = http://127.0.0.1:8007/review timeout = 10 + +[gateway] +url = http://127.0.0.1:8443/whoami +timeout = 5 diff --git a/dnfs/dnfs.py b/dnfs/dnfs.py index 9d19daa..1e83ad7 100755 --- a/dnfs/dnfs.py +++ b/dnfs/dnfs.py @@ -19,7 +19,6 @@ from waitress import serve import os import sys import json -import base64 import configparser import urllib.request import urllib.error @@ -48,6 +47,7 @@ _dnfs_config = { 'server': {'host': '', 'port': ''}, 'database': {'host': '', 'port': '', 'timeout': ''}, 'ai_review': {'url': '', 'timeout': ''}, + 'gateway': {'url': '', 'timeout': ''}, } @@ -93,12 +93,15 @@ if os.environ.get('DB_PORT'): _dnfs_config['database']['port'] = os.environ['DB_PORT'] if os.environ.get('REVIEW_URL'): _dnfs_config['ai_review']['url'] = os.environ['REVIEW_URL'] +if os.environ.get('GATEWAY_URL'): + _dnfs_config['gateway']['url'] = os.environ['GATEWAY_URL'] # 配置完整性校验 _required = { 'server': ['host', 'port'], 'database': ['host', 'port', 'timeout'], 'ai_review': ['url', 'timeout'], + 'gateway': ['url', 'timeout'], } for section, keys in _required.items(): for key in keys: @@ -113,11 +116,14 @@ _DBV2_ADDR = _dnfs_config['database']['host'] _DBV2_PORT = int(_dnfs_config['database']['port']) _REVIEW_SERVER_URL = _dnfs_config['ai_review']['url'] _REVIEW_TIMEOUT = int(_dnfs_config['ai_review']['timeout']) +_GATEWAY_URL = _dnfs_config['gateway']['url'] +_GATEWAY_TIMEOUT = int(_dnfs_config['gateway']['timeout']) _ADDR = _dnfs_config['server']['host'] _PORT = int(_dnfs_config['server']['port']) _config_source = f'confs (dnfs.ini @ {_confs_host}:{_confs_port})' print(f'[dnfs] 配置来源: {_config_source}') +print(f'[dnfs] Token 验签: gateway {_GATEWAY_URL}') # 管理员判定已上移至 gateway:登录时命中 [Admin] users 白名单即在 Token payload # 写入 is_admin=true,本服务通过 info.get('is_admin') 读取,不再本地维护名单。 @@ -301,14 +307,46 @@ apply_security_headers(app) CORS(app) -def decode_token(token: str): - idx = token.find('_') - if idx != -1: - raw = token[:idx] - # FIXME: 验证数字签名 - decoded = base64.b64decode(raw) - jj = decoded.decode('utf-8') - return json.loads(jj) +def decode_token(token: str): + """通过 gateway /whoami 接口验证 Token 并返回用户信息。 + + gateway /whoami 内部完成 RSA 签名验证和过期检查,返回: + - 成功: {"login": true, "user": "...", "is_admin": true/false, ...} + - 失败: {"login": false} + 网络异常(gateway 不可达)按 fail-closed 处理,抛 DnfsExp 拒绝请求。 + """ + if not token: + return None + + req = urllib.request.Request( + _GATEWAY_URL, + data=b'{}', + headers={ + "Content-Type": "application/json", + "Authorization": f"Bearer {token}", + }, + method="POST", + ) + + try: + with urllib.request.urlopen(req, timeout=_GATEWAY_TIMEOUT) as resp: + data = json.loads(resp.read().decode("utf-8")) + except (urllib.error.URLError, urllib.error.HTTPError, json.JSONDecodeError, OSError) as e: + # gateway 不可达或返回异常:fail-closed,拒绝请求 + logs.warning('dnfs', f'Token 验签失败(gateway 异常): {e}', extra={}) + raise DnfsExp('认证服务暂时不可用', 503) + except Exception as e: + logs.warning('dnfs', f'Token 验签未知异常: {e}', extra={}) + raise DnfsExp('认证失败', 401) + + if not data.get('login'): + return None + + # gateway 返回 login=true 时 user 字段必定存在 + return { + 'user': data.get('user', ''), + 'is_admin': bool(data.get('is_admin', False)), + } @app.route('/add_note', methods=['POST']) @@ -426,7 +464,7 @@ def get_notes(): # eg: {"id": 1} @app.route('/delete_note', methods=['POST']) - +@_handle_exp def delete_note(): data = request.get_json() @@ -529,7 +567,7 @@ def delete_note(): # "content": "hello" #} @app.route('/update_note', methods=['POST']) - +@_handle_exp def update_note(): data = request.get_json() diff --git a/docker-compose.yml b/docker-compose.yml index b1be30d..6401c09 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -83,6 +83,11 @@ services: - DB_HOST=db - DB_PORT=8005 - REVIEW_URL=http://ai_review:8007/review + - GATEWAY_URL=http://host.docker.internal:8443/whoami + - CONFS_HOST=host.docker.internal + - CONFS_PORT=8010 + extra_hosts: + - "host.docker.internal:host-gateway" depends_on: db: condition: service_healthy