安全:服务端校验钓鱼等待时间,防止前端篡改 wait_time
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 拒绝过早的收竿请求。
This commit is contained in:
@@ -92,8 +92,13 @@ class FishingController extends Controller
|
|||||||
$waitTime = rand($waitMin, $waitMax);
|
$waitTime = rand($waitMin, $waitMax);
|
||||||
$token = Str::random(32);
|
$token = Str::random(32);
|
||||||
$tokenKey = "fishing:token:{$user->id}";
|
$tokenKey = "fishing:token:{$user->id}";
|
||||||
// token 有效期 = 等待时间 + 10秒点击窗口 + 5秒缓冲
|
// token 有效期 = 等待时间 + 8秒点击窗口 + 5秒缓冲
|
||||||
Redis::setex($tokenKey, $waitTime + 15, $token);
|
// 同时把 cast 时间戳和 wait_time 一起存入,供 reel 做服务端时间校验
|
||||||
|
Redis::setex($tokenKey, $waitTime + 13, json_encode([
|
||||||
|
'token' => $token,
|
||||||
|
'cast_at' => time(),
|
||||||
|
'wait_time' => $waitTime,
|
||||||
|
]));
|
||||||
|
|
||||||
// 5. 生成随机浮漂坐标(百分比,避开边缘)
|
// 5. 生成随机浮漂坐标(百分比,避开边缘)
|
||||||
$bobberX = rand(15, 85); // 左右 15%~85%
|
$bobberX = rand(15, 85); // 左右 15%~85%
|
||||||
@@ -130,15 +135,33 @@ class FishingController extends Controller
|
|||||||
return response()->json(['status' => 'error', 'message' => '请先登录'], 401);
|
return response()->json(['status' => 'error', 'message' => '请先登录'], 401);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. 验证 token(防止脚本绕过浮漂直接收竿)
|
// 1. 验证 token + 服务端时间校验(防止前端篡改 wait_time 跳过等待)
|
||||||
$tokenKey = "fishing:token:{$user->id}";
|
$tokenKey = "fishing:token:{$user->id}";
|
||||||
$storedToken = Redis::get($tokenKey);
|
$storedJson = Redis::get($tokenKey);
|
||||||
$clientToken = $request->input('token', '');
|
$clientToken = $request->input('token', '');
|
||||||
|
|
||||||
if (! $storedToken || $storedToken !== $clientToken) {
|
if (! $storedJson) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'status' => 'error',
|
'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);
|
], 422);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user