From b7adf2993dd1828705a738b33eb6e36ba5e6862b Mon Sep 17 00:00:00 2001 From: yuetsh <517252939@qq.com> Date: Fri, 7 Aug 2026 01:59:27 -0600 Subject: [PATCH] =?UTF-8?q?fix(=E9=98=B6=E6=AE=B53):=20=E5=8E=BB=E6=8E=89?= =?UTF-8?q?=E5=88=A4=E9=A2=98=E6=9C=BA=20token=20=E7=9A=84=E5=BC=B1?= =?UTF-8?q?=E9=BB=98=E8=AE=A4=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit F5:token 校验实现本身是对的(用了 timingSafeEqual),问题是缺省值 "oj2-dev-token" 写死在仓库里 —— 写死在仓库里的 token 等于没有 token。 后端改为对齐旧后端 options/options.py:93 的 fail-safe: env 缺失时生成随机值并在启动日志里告警,判题机连不上,但不会静默用弱默认值。 compose 改为 ${OJ2_JUDGE_TOKEN:?...},未设置直接报错退出。 本地开发怎么设写在 docker/compose.dev.yml 顶部与 .env.example 里: 两个变量名不同(判题机镜像认 TOKEN,后端认 JUDGE_SERVER_TOKEN)但值必须相同。 Co-Authored-By: Claude Opus 5 --- .env.example | 7 ++++++- apps/api/src/config.ts | 21 ++++++++++++++++++++- docker/compose.dev.yml | 14 +++++++++++++- 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/.env.example b/.env.example index 5a66c27..d5bc974 100644 --- a/.env.example +++ b/.env.example @@ -1,7 +1,12 @@ DATABASE_URL=postgres://onlinejudge:onlinejudge@localhost:5433/onlinejudge REDIS_URL=redis://localhost:6380 JUDGE_SERVER_URL=http://localhost:8081 -JUDGE_SERVER_TOKEN=oj2-dev-token +# 判题机 token:自己生成,不要用固定值。 +# TOKEN=$(openssl rand -hex 32) +# echo "JUDGE_SERVER_TOKEN=$TOKEN" >> .env # 后端读这里 +# echo "OJ2_JUDGE_TOKEN=$TOKEN" >> docker/.env # 判题机容器读这里,两边必须一致 +# 留空的话后端会随机生成一个并在启动日志里告警,判题机心跳会被 403 挡掉。 +JUDGE_SERVER_TOKEN= JUDGE_CONCURRENCY=2 OJ2_DEV_USERNAME=student OJ2_DEV_PASSWORD=student123 diff --git a/apps/api/src/config.ts b/apps/api/src/config.ts index d785c4d..cb5b4cc 100644 --- a/apps/api/src/config.ts +++ b/apps/api/src/config.ts @@ -1,3 +1,22 @@ +import { randomBytes } from "node:crypto" + +/** + * 判题机 token。对齐旧后端 `options/options.py:93`: + * token = os.environ.get("JUDGE_SERVER_TOKEN"); return token if token else rand_str() + * env 缺失时生成随机值 fail-safe —— 宁可判题机连不上(启动时有明显告警), + * 也不要在仓库里写死一个人人都知道的弱默认值。 + */ +function judgeServerToken() { + const fromEnv = process.env.JUDGE_SERVER_TOKEN + if (fromEnv) return fromEnv + console.warn( + "[config] JUDGE_SERVER_TOKEN 未设置,已生成一次性随机 token。" + + "判题机将无法通过鉴权,本地开发请在 .env 里设置 JUDGE_SERVER_TOKEN," + + "并让 docker/compose.dev.yml 的 OJ2_JUDGE_TOKEN 取同一个值。", + ) + return randomBytes(32).toString("hex") +} + export const config = { port: Number(process.env.PORT ?? 3000), redisUrl: process.env.REDIS_URL ?? "redis://localhost:6380", @@ -5,7 +24,7 @@ export const config = { sessionTtlSeconds: Number(process.env.SESSION_TTL_SECONDS ?? 7 * 24 * 60 * 60), secureCookies: process.env.COOKIE_SECURE === "true", judgeServerUrl: process.env.JUDGE_SERVER_URL ?? "http://localhost:8081", - judgeServerToken: process.env.JUDGE_SERVER_TOKEN ?? "oj2-dev-token", + judgeServerToken: judgeServerToken(), judgeConcurrency: Number(process.env.JUDGE_CONCURRENCY ?? 2), avatarDirectory: process.env.AVATAR_DIRECTORY ?? "data/avatar", avatarUriPrefix: process.env.AVATAR_URI_PREFIX ?? "/public/avatar", diff --git a/docker/compose.dev.yml b/docker/compose.dev.yml index eeb71fa..310140d 100644 --- a/docker/compose.dev.yml +++ b/docker/compose.dev.yml @@ -6,6 +6,16 @@ # # PostgreSQL 固定 16,与生产版本(16.10)一致 —— 别图新换 17/18, # 开发环境用了生产不支持的特性,要到切换那天才炸。 +# +# 判题机 token 必须自己生成,仓库里不留默认值。第一次起服务前: +# +# TOKEN=$(openssl rand -hex 32) +# echo "OJ2_JUDGE_TOKEN=$TOKEN" >> docker/.env # docker compose 读这里 +# echo "JUDGE_SERVER_TOKEN=$TOKEN" >> .env # 后端读这里,两边必须一致 +# +# 两个变量名不同但值必须相同:TOKEN 是判题机镜像认的名字,JUDGE_SERVER_TOKEN 是后端认的名字。 +# 忘了设 OJ2_JUDGE_TOKEN 的话 docker compose 会直接报错退出;忘了设 JUDGE_SERVER_TOKEN +# 的话后端会随机生成一个并在启动日志里告警,判题机心跳会被 403 挡掉。 services: postgres: @@ -63,7 +73,9 @@ services: environment: SERVICE_URL: http://oj2-judge:8080 BACKEND_URL: http://host.docker.internal:3000/api/judge-server/heartbeat - TOKEN: ${OJ2_JUDGE_TOKEN:-oj2-dev-token} + # 没有默认值是故意的:写死在仓库里的弱 token 等于没有 token。 + # 未设置时 docker compose 直接报错,不会静默起一个人人可连的判题机。 + TOKEN: ${OJ2_JUDGE_TOKEN:?请先设置 OJ2_JUDGE_TOKEN,见本文件顶部注释} extra_hosts: - "host.docker.internal:host-gateway" mem_limit: 512m