63 lines
2.2 KiB
PHP
63 lines
2.2 KiB
PHP
<?php
|
|
|
|
namespace App\Rules;
|
|
|
|
use App\Services\RecaptchaV3;
|
|
use Closure;
|
|
use Illuminate\Contracts\Validation\ValidationRule;
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
class RecaptchaV3Rule implements ValidationRule
|
|
{
|
|
public function __construct(private string $action) {}
|
|
|
|
public function validate(string $attribute, mixed $value, Closure $fail): void
|
|
{
|
|
$token = (string) $value;
|
|
|
|
// ✅ 개발환경에서만 + 전용 로그파일
|
|
if (app()->environment(['local', 'development', 'staging'])) {
|
|
Log::channel('google_recaptcha')->info('[incoming]', [
|
|
'expected_action' => $this->action,
|
|
'attribute' => $attribute,
|
|
'token_len' => strlen($token),
|
|
'ip' => request()->ip(),
|
|
'path' => request()->path(),
|
|
]);
|
|
}
|
|
|
|
if ($token === '') {
|
|
$fail('보안 검증에 실패했습니다. 다시 시도해 주세요.');
|
|
return;
|
|
}
|
|
|
|
$svc = app(RecaptchaV3::class);
|
|
$data = $svc->verify($token, $this->action, request()->ip());
|
|
|
|
if (app()->environment(['local', 'development', 'staging'])) {
|
|
Log::channel('google_recaptcha')->info('[response]', [
|
|
'expected_action' => $this->action,
|
|
'success' => $data['success'] ?? null,
|
|
'score' => $data['score'] ?? null,
|
|
'action' => $data['action'] ?? null,
|
|
'hostname' => $data['hostname'] ?? null,
|
|
'error_codes' => $data['error-codes'] ?? null,
|
|
]);
|
|
}
|
|
|
|
if (!$svc->isPass($data, $this->action)) {
|
|
if (app()->environment(['local', 'development', 'staging'])) {
|
|
Log::channel('google_recaptcha')->warning('[failed]', [
|
|
'expected_action' => $this->action,
|
|
'got_action' => $data['action'] ?? null,
|
|
'score' => $data['score'] ?? null,
|
|
'success' => $data['success'] ?? null,
|
|
'error_codes' => $data['error-codes'] ?? null,
|
|
]);
|
|
}
|
|
|
|
$fail('보안 검증에 실패했습니다. 다시 시도해 주세요.');
|
|
}
|
|
}
|
|
}
|