From 0ea6ea206c32477579780ccaf77b728f67cb6f34 Mon Sep 17 00:00:00 2001 From: lkddi Date: Sun, 1 Mar 2026 16:33:32 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=89=E5=85=A8=EF=BC=9A=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E7=AB=AF=E6=A0=A1=E9=AA=8C=E9=92=93=E9=B1=BC=E7=AD=89=E5=BE=85?= =?UTF-8?q?=E6=97=B6=E9=97=B4=EF=BC=8C=E9=98=B2=E6=AD=A2=E5=89=8D=E7=AB=AF?= =?UTF-8?q?=E7=AF=A1=E6=94=B9=20wait=5Ftime?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cast() 将 {token, cast_at, wait_time} 以 JSON 存入 Redis; reel() 在验 token 后额外校验: elapsed = now() - cast_at >= wait_time - 1(含1秒容差) 未满足则返回422「鱼还没上钩,别急!」 即使用户通过 DevTools 将 wait_time 改为 0, 服务端仍按实际 wait_time 拒绝过早的收竿请求。 --- app/Http/Controllers/FishingController.php | 35 ++++++++++++++++++---- 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/app/Http/Controllers/FishingController.php b/app/Http/Controllers/FishingController.php index 15fdeb9..8e30994 100644 --- a/app/Http/Controllers/FishingController.php +++ b/app/Http/Controllers/FishingController.php @@ -92,8 +92,13 @@ class FishingController extends Controller $waitTime = rand($waitMin, $waitMax); $token = Str::random(32); $tokenKey = "fishing:token:{$user->id}"; - // token 有效期 = 等待时间 + 10秒点击窗口 + 5秒缓冲 - Redis::setex($tokenKey, $waitTime + 15, $token); + // token 有效期 = 等待时间 + 8秒点击窗口 + 5秒缓冲 + // 同时把 cast 时间戳和 wait_time 一起存入,供 reel 做服务端时间校验 + Redis::setex($tokenKey, $waitTime + 13, json_encode([ + 'token' => $token, + 'cast_at' => time(), + 'wait_time' => $waitTime, + ])); // 5. 生成随机浮漂坐标(百分比,避开边缘) $bobberX = rand(15, 85); // 左右 15%~85% @@ -130,15 +135,33 @@ class FishingController extends Controller return response()->json(['status' => 'error', 'message' => '请先登录'], 401); } - // 1. 验证 token(防止脚本绕过浮漂直接收竿) + // 1. 验证 token + 服务端时间校验(防止前端篡改 wait_time 跳过等待) $tokenKey = "fishing:token:{$user->id}"; - $storedToken = Redis::get($tokenKey); + $storedJson = Redis::get($tokenKey); $clientToken = $request->input('token', ''); - if (! $storedToken || $storedToken !== $clientToken) { + if (! $storedJson) { return response()->json([ 'status' => 'error', - 'message' => '鱼儿跑了!浮漂已超时或令牌无效,请重新抛竿。', + 'message' => '鱼儿跑了!浮漂已超时,请重新抛竿。', + ], 422); + } + + $stored = json_decode($storedJson, true); + // 校验 token 一致性 + if (($stored['token'] ?? '') !== $clientToken) { + return response()->json([ + 'status' => 'error', + 'message' => '令牌无效,请重新抛竿。', + ], 422); + } + // 校验服务端时间:距抛竿必须已过 wait_time 秒(允许 ±1s 误差) + $elapsed = time() - (int) ($stored['cast_at'] ?? 0); + $required = (int) ($stored['wait_time'] ?? 0); + if ($elapsed < $required - 1) { + return response()->json([ + 'status' => 'error', + 'message' => '鱼还没上钩,别急!', ], 422); }