'use strict'; /** * 后端配置共用:JWT 签名密钥解析(**唯一入口**) * ============================================================ * * 为什么单独成文件(R105 / 2026-10-12,D1) * ------------------------------------------------------------ * 原实现(config.default.js 旧 L300/L304)为: * const DEFAULT_JWT_SECRET = '<32 位明文串>'; * secret: process.env.JWT_SECRET || DEFAULT_JWT_SECRET * 这是**可预测的明文兜底**:密钥缺失时静默使用一个**已入库、任何人可读**的常量, * 且该常量与 `scripts/.creds.local.json` 的 `jwt_fallback_secret` 同值 ⇒ 等价于 * 「JWT 签名密钥公开」。它与仓库既有纪律直接冲突: * · `scripts/_creds.py` / `backend/scripts/_db.cjs` 早已统一为「缺凭据明确失败」; * · config.default.js:459 对 DB_PASSWORD 也已改为「缺失即阻断」。 * 同族问题若各自实现,必然漂移(R104 的结论)⇒ 收敛到**唯一解析源**,不留兜底。 * * 读取优先级(与 `_db.cjs` / `_creds.py` 同一纪律) * ------------------------------------------------------------ * 1. 环境变量 JWT_SECRET(生产 systemd / 启动器注入优先;本地由 config.default.js * 顶部的 loadDeployEnv() 从 deploy/.env.production 注入,非跳过键) * 2. 仓库根 `scripts/.creds.local.json` 的 `jwt_fallback_secret`(本机开发, * .gitignore 已排除;也是 scripts/_fixtures.py 的末级兜底来源,两侧同源 ⇒ * 自签 token 与后端验签必然一致) * 3. 明确失败 —— ⛔ **刻意不提供明文兜底**:抛错并给出修复指引。 * 理由:密钥缺失属"注入链路断了"的真实故障,明确阻断远优于静默用公开值签名。 * * ⚠️ 与 Python 侧 `scripts/_creds.py`(JWT_FALLBACK_SECRET)口径必须一致; * 如需增删键,两边同时改。 * * @returns {string} 生效中的 JWT 签名密钥 */ const fs = require('fs'); const path = require('path'); const LOCAL_FILE = path.join(__dirname, '..', '..', 'scripts', '.creds.local.json'); function _loadLocal() { try { return JSON.parse(fs.readFileSync(LOCAL_FILE, 'utf8')); } catch (e) { if (e.code === 'ENOENT') return {}; // 文件不存在合法(走环境变量) // 损坏必须报错,不静默降级 throw new Error(`凭据文件解析失败 ${LOCAL_FILE}: ${e.message}`); } } function loadJwtSecret() { const fromEnv = process.env.JWT_SECRET; if (fromEnv) return fromEnv; const local = _loadLocal(); const fromFile = local.jwt_fallback_secret; if (fromFile) return fromFile; throw new Error( '[配置缺失] 未取到 JWT_SECRET —— 无法安全地签名/校验登录态。请任选其一:\n' + ' ① 设置环境变量 JWT_SECRET(生产/CI 推荐,deploy/.env.production 已含该键);\n' + ` ② 在 ${LOCAL_FILE} 写入键 "jwt_fallback_secret"(本机开发用,该文件已被 .gitignore 排除);\n` + '⛔ 本模块刻意不提供明文兜底:原 config.default.js 的内置密钥已移除,\n' + ' 因为「可预测的公开密钥」等价于「JWT 可被任意伪造」。' ); } module.exports = { loadJwtSecret };