mirror of
https://github.com/Silentely/eSIM-Tools.git
synced 2026-09-03 06:24:20 +08:00
feat: 为 Turnstile 验证添加强制执行控制功能
- 新增 TURNSTILE_ENFORCE 环境变量,可在紧急情况下跳过 BFF Turnstile 验证 - 在 CLAUDE.md 和 env.example 中添加完整的 Turnstile 配置说明 - 修改 BFF 代理逻辑,仅在启用强制执行时进行 Turnstile 验证 - 优化前端 JavaScript 代码,移除冗余的验证状态变量 - 在跳过验证时输出警告日志,便于调试和监控
This commit is contained in:
@@ -601,8 +601,11 @@ ALLOWED_ORIGIN=https://esim.cosr.eu.org
|
||||
# 🔐 生成强随机密钥: openssl rand -hex 32
|
||||
ACCESS_KEY=your_strong_random_key_here
|
||||
|
||||
# Cloudflare Turnstile 站点密钥 (可选)
|
||||
# Cloudflare Turnstile 配置 (可选)
|
||||
TURNSTILE_SITE_KEY=0x4AAAAAAA...
|
||||
TURNSTILE_SECRET_KEY=1x0000000000000000000000000000000
|
||||
# 设为 false 可在紧急情况下跳过 BFF Turnstile 校验
|
||||
TURNSTILE_ENFORCE=true
|
||||
|
||||
# Node 环境
|
||||
NODE_ENV=development # development | production
|
||||
|
||||
@@ -17,6 +17,12 @@ LOG_LEVEL=info
|
||||
CORS_ORIGIN=*
|
||||
ALLOWED_ORIGIN=https://esim.cosr.eu.org
|
||||
|
||||
# Cloudflare Turnstile 配置
|
||||
TURNSTILE_SITE_KEY=
|
||||
TURNSTILE_SECRET_KEY=
|
||||
# 设为 false 可在紧急情况下跳过 BFF Turnstile 校验(默认 true)
|
||||
TURNSTILE_ENFORCE=true
|
||||
|
||||
# 受保护函数访问密钥(要求调用方在 Header x-esim-key 或 body.authKey / ?authKey 携带匹配值)
|
||||
# ⚠️ 必填:Server 与 Functions/BFF 共享的访问密钥
|
||||
# 🔐 生成强随机密钥: openssl rand -hex 32
|
||||
|
||||
@@ -31,9 +31,13 @@ export default async (request, context) => {
|
||||
});
|
||||
}
|
||||
|
||||
// Turnstile 校验(可选,若配置了 TURNSTILE_SECRET_KEY 则启用)
|
||||
// Turnstile 校验(可选,可通过 TURNSTILE_ENFORCE=false 关闭)
|
||||
const turnstileSecret = (typeof Deno !== 'undefined' && Deno.env && Deno.env.get('TURNSTILE_SECRET_KEY')) || '';
|
||||
if (turnstileSecret) {
|
||||
const turnstileEnforceRaw = (typeof Deno !== 'undefined' && Deno.env && Deno.env.get('TURNSTILE_ENFORCE')) || 'true';
|
||||
const turnstileEnforce = String(turnstileEnforceRaw).toLowerCase() !== 'false';
|
||||
const shouldCheckTurnstile = turnstileSecret && turnstileEnforce;
|
||||
|
||||
if (shouldCheckTurnstile) {
|
||||
try {
|
||||
// 读取前端传来的 token(body 或 header)
|
||||
let cfToken = request.headers.get('x-cf-turnstile') || '';
|
||||
@@ -72,6 +76,8 @@ export default async (request, context) => {
|
||||
} catch (_) {
|
||||
return new Response(JSON.stringify({ error: 'Turnstile Check Error' }), { status: 403, headers: { 'Content-Type': 'application/json' } });
|
||||
}
|
||||
} else if (turnstileSecret && !turnstileEnforce) {
|
||||
console.warn('[BFF] Turnstile enforcement disabled via TURNSTILE_ENFORCE=false, skipping challenge.');
|
||||
}
|
||||
|
||||
// 复制请求头并添加服务端密钥头(仅内部互调使用,不影响浏览器 CORS)
|
||||
@@ -119,4 +125,3 @@ export default async (request, context) => {
|
||||
// 直接透传响应
|
||||
return response;
|
||||
};
|
||||
|
||||
|
||||
@@ -47,7 +47,6 @@ if (window.TURNSTILE_SITE_KEY) {
|
||||
};
|
||||
|
||||
let widgetId = null;
|
||||
let hasExecuted = false;
|
||||
let executing = false;
|
||||
|
||||
const exposeRefreshHandle = () => {
|
||||
@@ -77,21 +76,20 @@ const markIdle = () => { executing = false; };
|
||||
markIdle();
|
||||
};
|
||||
|
||||
const runExecute = () => {
|
||||
if (!window.turnstile || widgetId == null || executing) {
|
||||
return;
|
||||
}
|
||||
executing = true;
|
||||
if (hasExecuted) {
|
||||
try { window.turnstile.reset(widgetId); } catch (_) {}
|
||||
}
|
||||
hasExecuted = true;
|
||||
try {
|
||||
window.turnstile.execute(widgetId);
|
||||
} catch (_) {
|
||||
markIdle();
|
||||
}
|
||||
};
|
||||
const runExecute = () => {
|
||||
if (!window.turnstile || widgetId == null || executing) {
|
||||
return;
|
||||
}
|
||||
executing = true;
|
||||
try {
|
||||
window.turnstile.reset(widgetId);
|
||||
} catch (_) {}
|
||||
try {
|
||||
window.turnstile.execute(widgetId);
|
||||
} catch (_) {
|
||||
markIdle();
|
||||
}
|
||||
};
|
||||
|
||||
const initTurnstile = () => {
|
||||
if (!window.turnstile) {
|
||||
|
||||
Reference in New Issue
Block a user