diff --git a/app/Http/Controllers/Admin/AuthController.php b/app/Http/Controllers/Admin/AuthController.php index f7b2208..1a2cb5a 100644 --- a/app/Http/Controllers/Admin/AuthController.php +++ b/app/Http/Controllers/Admin/AuthController.php @@ -13,6 +13,7 @@ use Illuminate\Http\Request; use Illuminate\Support\Facades\App; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Hash; +use Illuminate\Support\Facades\RateLimiter; use Illuminate\Support\Facades\Validator; use Illuminate\Validation\ValidationException; use Tymon\JWTAuth\Facades\JWTAuth; @@ -28,6 +29,22 @@ class AuthController extends Controller */ protected OperationLogService $logService; + /** + * 最大允许的失败尝试次数 + */ + protected function maxAttempts(): int + { + return 5; // 5次失败后锁定 + } + + /** + * 锁定时间(分钟) + */ + protected function decayMinutes(): int + { + return 60; // 锁定60秒 + } + /** * 构造函数 * @param ApiResponseService $responseService @@ -51,7 +68,21 @@ class AuthController extends Controller public function login(Request $request): JsonResponse { $credentials = $request->only('username', 'password'); + $throttleKey = strtolower($request->input('username')) . '|' + . $request->ip(); try { + if (RateLimiter::tooManyAttempts( + $throttleKey, + $this->maxAttempts() + ) + ) { + $seconds = RateLimiter::availableIn($throttleKey); + throw new \Exception( + __validation('auth.count1') . ' ' . ceil( + $seconds / $this->decayMinutes() + ) . ' ' . __validation('auth.count2') + ); + } $validator = Validator::make($request->all(), [ 'username' => 'required', 'password' => 'required' @@ -93,8 +124,10 @@ class AuthController extends Controller 'token' => $token, ]); } catch (ValidationException|CustomException $e) { + RateLimiter::hit($throttleKey, $this->decayMinutes()); throw $e; } catch (\Exception $e) { + RateLimiter::hit($throttleKey, $this->decayMinutes()); $m_prefix = __('admin.login') . __('admin.failed'); return $this->responseService->systemError($m_prefix . ':' . $e->getMessage()); } diff --git a/resources/lang/en/validation.php b/resources/lang/en/validation.php index 191b646..89b3686 100644 --- a/resources/lang/en/validation.php +++ b/resources/lang/en/validation.php @@ -6,7 +6,9 @@ return [ 'p_empty' => 'Password cannot be empty', 'u_p_error' => 'Account or password error', 'u_disabled' => 'Account has been disabled', - 'invalid' => 'Invalid Credentials' + 'invalid' => 'Invalid Credentials', + 'count1' => 'Too many login attempts; please try again in', + 'count2' => 'minutes.' ], 'admin_user' => [ 'l_a_empty' => 'Login account cannot be empty', diff --git a/resources/lang/zh-CN/validation.php b/resources/lang/zh-CN/validation.php index d611777..301f546 100644 --- a/resources/lang/zh-CN/validation.php +++ b/resources/lang/zh-CN/validation.php @@ -6,7 +6,9 @@ return [ 'p_empty' => '密码不能为空', 'u_p_error' => '账号或密码错误', 'u_disabled' => '账号已被禁用', - 'invalid' => '无效的凭据' + 'invalid' => '无效的凭据', + 'count1' => '登录尝试次数过多,请在', + 'count2' => '分钟后重试。' ], 'admin_user' => [ 'l_a_empty' => '登录账号不能为空', diff --git a/resources/lang/zh-TW/validation.php b/resources/lang/zh-TW/validation.php index 10e14c4..d38dea3 100644 --- a/resources/lang/zh-TW/validation.php +++ b/resources/lang/zh-TW/validation.php @@ -6,7 +6,9 @@ return [ 'p_empty' => '密碼不能為空', 'u_p_error' => '帳號或密碼錯誤', 'u_disabled' => '帳號已被禁用', - 'invalid' => '無效的憑證' + 'invalid' => '無效的憑證', + 'count1' => '登入嘗試次數過多,請在', + 'count2' => '分鐘後重試。' ], 'admin_user' => [ 'l_a_empty' => '登入帳號不能為空',