From 00c8b2e1d185ac5d0539dc1d729ec19ab2ffb8cc Mon Sep 17 00:00:00 2001 From: Boen_Shi Date: Thu, 2 Jul 2026 20:04:36 +0800 Subject: [PATCH] Remove captcha login flow and allow user email updates --- .../Controllers/Api/Admin/UserController.php | 2 ++ app/Http/Controllers/Api/AuthController.php | 18 +------------ frontend/src/api/admin.ts | 1 + frontend/src/api/auth.ts | 5 ---- frontend/src/views/LoginView.vue | 27 ------------------- frontend/src/views/admin/UsersView.vue | 1 + frontend/src/views/app/ResourcesView.vue | 6 ++--- 7 files changed, 8 insertions(+), 52 deletions(-) diff --git a/app/Http/Controllers/Api/Admin/UserController.php b/app/Http/Controllers/Api/Admin/UserController.php index fe77d62..5fd8f55 100644 --- a/app/Http/Controllers/Api/Admin/UserController.php +++ b/app/Http/Controllers/Api/Admin/UserController.php @@ -13,6 +13,7 @@ use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Str; +use Illuminate\Validation\Rule; #[Apidoc\Group('后台')] #[Apidoc\Title('用户与邀请码')] @@ -63,6 +64,7 @@ final class UserController extends Controller $user = $this->resolveUser($user); $data = $request->validate([ 'name' => ['sometimes', 'string', 'max:50'], + 'email' => ['sometimes', 'email', 'max:120', Rule::unique('users', 'email')->ignore($user->id)], 'role' => ['sometimes', 'in:admin,teacher,user'], 'is_active' => ['sometimes', 'boolean'], 'password' => ['nullable', 'string', 'min:6'], diff --git a/app/Http/Controllers/Api/AuthController.php b/app/Http/Controllers/Api/AuthController.php index 0641cab..854d6a2 100644 --- a/app/Http/Controllers/Api/AuthController.php +++ b/app/Http/Controllers/Api/AuthController.php @@ -13,7 +13,6 @@ use hg\apidoc\annotation as Apidoc; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\Hash; -use Illuminate\Support\Facades\RateLimiter; use Illuminate\Validation\ValidationException; use Tymon\JWTAuth\Facades\JWTAuth; @@ -76,38 +75,23 @@ final class AuthController extends Controller $data = $request->validate([ 'email' => ['required', 'email'], 'password' => ['required', 'string'], - 'captcha' => ['nullable', 'string'], ]); - $key = 'login:'.$request->ip().':'.$data['email']; $user = User::query()->where('email', $data['email'])->first(); - if (RateLimiter::tooManyAttempts($key, 5) || ($user?->failed_login_count ?? 0) >= 5) { - $captcha = (string) ($data['captcha'] ?? ''); - $expectedCaptcha = (string) session('captcha', ''); - - if ($captcha === '' || $expectedCaptcha === '' || $captcha !== $expectedCaptcha) { - return ApiResponse::error('请输入验证码', 429, 429, ['captcha_required' => true]); - } - } - if (! $user || ! Hash::check($data['password'], $user->password)) { - RateLimiter::hit($key, 300); $user?->update([ 'failed_login_count' => $user->failed_login_count + 1, 'last_failed_login_at' => now(), ]); - return ApiResponse::error('账号或密码错误', 422, 422, [ - 'captcha_required' => RateLimiter::attempts($key) >= 5, - ]); + return ApiResponse::error('账号或密码错误', 422, 422); } if (! $user->is_active) { return ApiResponse::error('账号已被禁用', 403, 403); } - RateLimiter::clear($key); $user->update(['failed_login_count' => 0, 'last_login_at' => now()]); OperationLog::create([ diff --git a/frontend/src/api/admin.ts b/frontend/src/api/admin.ts index fa6c463..f3fd3e7 100644 --- a/frontend/src/api/admin.ts +++ b/frontend/src/api/admin.ts @@ -189,6 +189,7 @@ export function createUser(payload: { name: string; email: string; password: str export function updateUser(userId: number, payload: { name?: string + email?: string role?: string is_active?: boolean password?: string diff --git a/frontend/src/api/auth.ts b/frontend/src/api/auth.ts index a68e6d0..7a84c82 100644 --- a/frontend/src/api/auth.ts +++ b/frontend/src/api/auth.ts @@ -4,7 +4,6 @@ import type { User } from '@/types/api' export interface LoginPayload { email: string password: string - captcha?: string } export interface LoginResult { @@ -31,7 +30,3 @@ export function register(payload: { export function me() { return apiGet('/api/auth/me') } - -export function captcha() { - return apiGet<{ captcha: string; expires_in: number }>('/api/auth/captcha') -} diff --git a/frontend/src/views/LoginView.vue b/frontend/src/views/LoginView.vue index 89a3fec..9a6b74a 100644 --- a/frontend/src/views/LoginView.vue +++ b/frontend/src/views/LoginView.vue @@ -3,17 +3,13 @@ import { reactive, shallowRef } from 'vue' import { useRouter } from 'vue-router' import { ElMessage } from 'element-plus' import { useAuthStore } from '@/stores/auth' -import { captcha } from '@/api/auth' const router = useRouter() const auth = useAuthStore() const loading = shallowRef(false) -const captchaText = shallowRef('') -const captchaRequired = shallowRef(false) const form = reactive({ email: '', password: '', - captcha: '', }) async function submit() { @@ -22,22 +18,11 @@ async function submit() { await auth.login(form.email, form.password) ElMessage.success('登录成功') await router.push('/quiz') - } catch (error: any) { - captchaRequired.value = Boolean(error.response?.data?.data?.captcha_required) - if (captchaRequired.value) { - await loadCaptcha() - } - throw error } finally { loading.value = false } } -async function loadCaptcha() { - const response = await captcha() - captchaText.value = response.data.captcha -} -