'require|in:' . AdminTerminalEnum::PC . ',' . AdminTerminalEnum::MOBILE, 'account' => 'require', 'password' => 'require|password', ]; protected $message = [ 'account.require' => '请输入账号', 'password.require' => '请输入密码' ]; /** * @notes @notes 密码验证 * @param $password * @param $other * @param $data * @return bool|string * @throws \think\db\exception\DataNotFoundException * @throws \think\db\exception\DbException * @throws \think\db\exception\ModelNotFoundException * @author 令狐冲 * @date 2021/7/2 14:00 */ public function password($password, $other, $data) { // 本地调试:免密登录开关。须同时满足 APP_DEBUG=1 + LOCAL_DEBUG.LOGIN_SKIP_PASSWORD=true, // 任一不满足都走原始密码校验。每次免密登录都会记一条 warning 日志,便于发现误开。 if (self::isLocalDebugLoginBypassEnabled()) { $adminInfo = Admin::where('account', '=', $data['account']) ->field(['password,disable']) ->findOrEmpty(); if ($adminInfo->isEmpty()) { return '账号不存在'; } if ((int)($adminInfo['disable'] ?? 0) === 1) { return '账号已禁用'; } Log::warning(sprintf( '[LOCAL_DEBUG] login bypass password: account=%s, ip=%s', (string)($data['account'] ?? ''), (string)request()->ip() )); return true; } // 登录限制 $config = [ 'login_restrictions' => ConfigService::get('admin_login', 'login_restrictions'), 'password_error_times' => ConfigService::get('admin_login', 'password_error_times'), 'limit_login_time' => ConfigService::get('admin_login', 'limit_login_time'), ]; $adminAccountSafeCache = new AdminAccountSafeCache(); if ($config['login_restrictions'] == 1) { $adminAccountSafeCache->count = $config['password_error_times']; $adminAccountSafeCache->minute = $config['limit_login_time']; } //后台账号安全机制,连续输错后锁定,防止账号密码暴力破解 if ($config['login_restrictions'] == 1 && !$adminAccountSafeCache->isSafe()) { return '密码连续' . $adminAccountSafeCache->count . '次输入错误,请' . $adminAccountSafeCache->minute . '分钟后重试'; } $adminInfo = Admin::where('account', '=', $data['account']) ->field(['password,disable']) ->findOrEmpty(); if ($adminInfo->isEmpty()) { return '账号不存在'; } if ($adminInfo['disable'] === 1) { return '账号已禁用'; } if (empty($adminInfo['password'])) { $adminAccountSafeCache->record(); return '账号不存在'; } $passwordSalt = Config::get('project.unique_identification'); if ($adminInfo['password'] !== create_password($password, $passwordSalt)) { $adminAccountSafeCache->record(); return '密码错误'; } $adminAccountSafeCache->relieve(); return true; } /** * 是否启用"本地调试免密登录"。 * * 双重闸门: * 1. APP_DEBUG 必须为 true(即应用本身处于调试模式) * 2. LOCAL_DEBUG.LOGIN_SKIP_PASSWORD 必须显式为 true / 1 / yes / on * * 任何一条不满足都返回 false,强制走原始密码校验。这样即使误把 * LOGIN_SKIP_PASSWORD 配进生产,只要 APP_DEBUG=0 仍然安全。 */ private static function isLocalDebugLoginBypassEnabled(): bool { $appDebug = strtolower((string) env('app_debug', '0')); if (!in_array($appDebug, ['1', 'true', 'yes', 'on'], true)) { return false; } $flag = strtolower((string) env('local_debug.login_skip_password', '')); return in_array($flag, ['1', 'true', 'yes', 'on'], true); } }