eat: add reCAPTCHA v3 and Cloudflare Turnstile verification support

- Implement reCAPTCHA v3 with score-based validation
- Add Cloudflare Turnstile as captcha alternative
- Create reusable CaptchaService for unified validation
- Support switching between recaptcha, recaptcha-v3, and turnstile
- Maintain backward compatibility with existing configurations
This commit is contained in:
xboard
2025-06-28 18:01:59 +08:00
parent f1d1dd5684
commit 6d85736eea
18 changed files with 1097 additions and 836 deletions
@@ -18,11 +18,17 @@ class CommController extends Controller
'email_whitelist_suffix' => (int) admin_setting('email_whitelist_enable', 0)
? Helper::getEmailSuffix()
: 0,
'is_recaptcha' => (int) admin_setting('recaptcha_enable', 0) ? 1 : 0,
'is_captcha' => (int) admin_setting('captcha_enable', 0) ? 1 : 0,
'captcha_type' => admin_setting('captcha_type', 'recaptcha'),
'recaptcha_site_key' => admin_setting('recaptcha_site_key'),
'recaptcha_v3_site_key' => admin_setting('recaptcha_v3_site_key'),
'recaptcha_v3_score_threshold' => admin_setting('recaptcha_v3_score_threshold', 0.5),
'turnstile_site_key' => admin_setting('turnstile_site_key'),
'app_description' => admin_setting('app_description'),
'app_url' => admin_setting('app_url'),
'logo' => admin_setting('logo'),
// 保持向后兼容
'is_recaptcha' => (int) admin_setting('captcha_enable', 0) ? 1 : 0,
];
return $this->success($data);
}
@@ -40,7 +40,7 @@ class AuthController extends Controller
]);
[$success, $result] = $this->mailLinkService->handleMailLink(
$params['email'],
$params['email'],
$request->input('redirect')
);
@@ -92,39 +92,39 @@ class AuthController extends Controller
// 处理直接通过token重定向
if ($token = $request->input('token')) {
$redirect = '/#/login?verify=' . $token . '&redirect=' . ($request->input('redirect', 'dashboard'));
return redirect()->to(
admin_setting('app_url')
? admin_setting('app_url') . $redirect
: url($redirect)
? admin_setting('app_url') . $redirect
: url($redirect)
);
}
// 处理通过验证码登录
if ($verify = $request->input('verify')) {
$userId = $this->mailLinkService->handleTokenLogin($verify);
if (!$userId) {
return response()->json([
'message' => __('Token error')
], 400);
}
$user = \App\Models\User::find($userId);
if (!$user) {
return response()->json([
'message' => __('User not found')
], 400);
}
$authService = new AuthService($user);
return response()->json([
'data' => $authService->generateAuthData()
]);
}
return response()->json([
'message' => __('Invalid request')
], 400);
@@ -136,7 +136,7 @@ class AuthController extends Controller
public function getQuickLoginUrl(Request $request)
{
$authorization = $request->input('auth_data') ?? $request->header('authorization');
if (!$authorization) {
return response()->json([
'message' => ResponseEnum::CLIENT_HTTP_UNAUTHORIZED
@@ -144,14 +144,14 @@ class AuthController extends Controller
}
$user = AuthService::findUserByBearerToken($authorization);
if (!$user) {
return response()->json([
'message' => ResponseEnum::CLIENT_HTTP_UNAUTHORIZED_EXPIRED
], 401);
}
$url = $this->mailLinkService->getQuickLoginUrl($user, $request->input('redirect'));
$url = $this->loginService->generateQuickLoginUrl($user, $request->input('redirect'));
return $this->success($url);
}
@@ -7,23 +7,22 @@ use App\Http\Requests\Passport\CommSendEmailVerify;
use App\Jobs\SendEmailJob;
use App\Models\InviteCode;
use App\Models\User;
use App\Services\CaptchaService;
use App\Utils\CacheKey;
use App\Utils\Helper;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use ReCaptcha\ReCaptcha;
class CommController extends Controller
{
public function sendEmailVerify(CommSendEmailVerify $request)
{
if ((int) admin_setting('recaptcha_enable', 0)) {
$recaptcha = new ReCaptcha(admin_setting('recaptcha_key'));
$recaptchaResp = $recaptcha->verify($request->input('recaptcha_data'));
if (!$recaptchaResp->isSuccess()) {
return $this->fail([400, __('Invalid code is incorrect')]);
}
// 验证人机验证码
$captchaService = app(CaptchaService::class);
[$captchaValid, $captchaError] = $captchaService->verify($request);
if (!$captchaValid) {
return $this->fail($captchaError);
}
$email = $request->input('email');
@@ -10,6 +10,7 @@ use App\Models\Order;
use App\Models\Plan;
use App\Models\Ticket;
use App\Models\User;
use App\Services\Auth\LoginService;
use App\Services\AuthService;
use App\Services\UserService;
use App\Utils\CacheKey;
@@ -19,6 +20,14 @@ use Illuminate\Support\Facades\Cache;
class UserController extends Controller
{
protected $loginService;
public function __construct(
LoginService $loginService
) {
$this->loginService = $loginService;
}
public function getActiveSession(Request $request)
{
$user = User::find($request->user()->id);
@@ -205,15 +214,7 @@ class UserController extends Controller
return $this->fail([400, __('The user does not exist')]);
}
$code = Helper::guid();
$key = CacheKey::get('TEMP_TOKEN', $code);
Cache::put($key, $user->id, 60);
$redirect = '/#/login?verify=' . $code . '&redirect=' . ($request->input('redirect') ? $request->input('redirect') : 'dashboard');
if (admin_setting('app_url')) {
$url = admin_setting('app_url') . $redirect;
} else {
$url = url($redirect);
}
$url = $this->loginService->generateQuickLoginUrl($user, $request->input('redirect'));
return $this->success($url);
}
}