laragear/recaptcha

使用异步HTTP/3集成reCAPTCHA,用几行代码使您的应用快速。

v2.1.0 2024-04-10 18:02 UTC

This package is auto-updated.

Last update: 2024-09-10 18:47:07 UTC


README

Latest Version on Packagist Latest stable test run Codecov coverage Maintainability Sonarcloud Status Laravel Octane Compatibility

使用异步HTTP/3 async HTTP/3 集成reCAPTCHA,用几行代码使您的应用 快速

use Illuminate\Support\Facades\Route;

Route::post('login', function () {
    // ...
})->middleware('recaptcha:checkbox');

成为赞助商

您的支持使我能够保持这个包免费、更新和可维护。或者,您可以 传播这个消息!

要求

  • Laravel 10 或更高版本

安装

您可以通过Composer安装此包

composer require laragear/recaptcha

设置

将您网站的reCAPTCHA密钥添加到项目的环境文件中。您可以分别为reCAPTCHA v2的 复选框隐藏Android评分 添加每个密钥。

如果您还没有,可以在您的 reCAPTCHA 管理面板 中生成它。

RECAPTCHA_CHECKBOX_SECRET=6t5geA1UAAAAAN...
RECAPTCHA_CHECKBOX_KEY=6t5geA1UAAAAAN...

RECAPTCHA_INVISIBLE_SECRET=6t5geA2UAAAAAN...
RECAPTCHA_INVISIBLE_KEY=6t5geA2UAAAAAN...

RECAPTCHA_ANDROID_SECRET=6t5geA3UAAAAAN...
RECAPTCHA_ANDROID_KEY=6t5geA3UAAAAAN...

RECAPTCHA_SCORE_SECRET=6t5geA4UAAAAAN...
RECAPTCHA_SCORE_KEY=6t5geA4UAAAAAN...

这允许您在不同的环境中使用同一应用程序检查不同的reCAPTCHA机制。

注意

ReCaptcha已经包含了v2密钥用于本地开发。对于v3,您需要在生产环境中创建自己的凭证集。

用法

用法取决于您是否使用复选框、隐藏、Android挑战或v3评分驱动挑战。

复选框、隐藏和Android挑战

在将reCAPTCHA集成到您的前端或Android应用后,在提交带有reCAPTCHA的表单的 POST 路由中设置ReCaptcha中间件。中间件将捕获 g-recaptcha-response 输入(您稍后可以更改它)并检查它是否有效。

只需使用 ReCaptcha 中间件构建器即可声明中间件

  • ReCaptcha::checkbox() 用于显式渲染的复选框挑战。
  • ReCaptcha::invisible() 用于隐藏挑战。
  • ReCaptcha::android() 用于Android应用挑战。
use App\Http\Controllers\ContactController;
use Laragear\ReCaptcha\Http\Middleware\Builders\ReCaptcha;
use Illuminate\Support\Facades\Route;

Route::post('contact', [ContactController::class, 'send'])
    ->middleware(ReCaptcha::invisible()->forGuests('web')->remember())

如果由于某种原因挑战未成功,验证将立即启动并抛出 ValidationException,将用户返回到表单。

记住挑战

为了避免反复要求挑战,您可以“记住”给定分钟的挑战。这可以在全局范围内启用,但您可能更喜欢按路由基础进行。

简单使用 remember() 方法。您可以设置分钟数以覆盖全局参数。或者,rememberForever() 将永远记住挑战。

use App\Http\Controllers\Auth\LoginController;
use Laragear\ReCaptcha\Http\Middleware\Builders\ReCaptcha;
use Illuminate\Support\Facades\Route

Route::post('login', [LoginController::class, 'login'])
     ->middleware(ReCaptcha::invisible()->remember());

Route::post('message', [ChatController::class, 'login'])
     ->middleware(ReCaptcha::checkbox()->rememberForever());

您应该将此与Blade模板中的 @robot 指令结合使用,以便在用户之前未成功完成挑战时渲染挑战。

@robot
  <div class="g-recaptcha"
       data-sitekey="{{ recaptcha('invisible') }}"
       data-callback="onSubmit"
       data-size="invisible">
  </div>
@endrobot

提示

将挑战记住几分钟的好地方是预期会失败的表单,或者当用户可能在多个表单之间跳转时。

更改输入名称

您可以使用 input() 将输入名称从默认的 g-recaptcha-response 更改为任何内容。

use App\Http\Controllers\Auth\LoginController;
use Laragear\ReCaptcha\Http\Middleware\Builders\ReCaptcha;
use Illuminate\Support\Facades\Route

Route::post('login', [LoginController::class, 'login'])
     ->middleware(ReCaptcha::checkbox()->input('recaptcha_input'));

评分驱动挑战

reCAPTCHA v3 中间件的工作方式与 v2 不同。此响应始终是成功,但挑战分数介于 0.01.0 之间。类似人类的交互将更高,而机器人的分数将更低。默认阈值是 0.5,但可以在全局或按路由更改。

要开始使用它,只需在您的路由中使用 ReCaptcha::score() 方法。

use App\Http\Controllers\CommentController;
use Laragear\ReCaptcha\Http\Middleware\Builders\ReCaptcha;
use Illuminate\Support\Facades\Route

Route::post('comment', [CommentController::class, 'create'])
     ->middleware(ReCaptcha::score());

一旦挑战已发送到您的控制器,您将能够访问请求类或实例的两种方法:isHuman()isRobot(),它们可能返回 truefalse

use App\Models\Post;
use Illuminate\Http\Request;

public function store(Request $request, Post $post)
{
    $request->validate([
        'body' => 'required|string|max:255'
    ]);
    
    $comment = $post->comment()->make($request->only('body'));
    
    // Flag the comment as "moderated" if it was a written by robot.
    if ($request->isRobot()) {
        $comment->markAsModerated();
    }
    
    $comment->save();
    
    return view('post.comment.show', ['comment' => $comment]);
}

您还可以通过 ReCaptcha 门面的 response() 方法访问 reCAPTCHA 的响应

use Laragear\ReCaptcha\Facades\ReCaptcha;

$response = ReCaptcha::response();

if ($response->score > 0.2) {
    return 'Try again!';
}

警告

请小心调用 response(),因为它会在没有挑战的控制器上抛出异常。

阈值、操作和输入名称

中间件通过中间件助手接受三个额外的参数。

  1. threshold():必须大于或等于此值才能被认为是人类。
  2. action():可选地检查的操作名称。
  3. input():要验证的 reCAPTCHA 输入的名称。
use App\Http\Controllers\CommentController;
use Laragear\ReCaptcha\Http\Middleware\Builders\ReCaptcha;
use Illuminate\Support\Facades\Route

Route::post('comment', [CommentController::class, 'store'])
     ->middleware(ReCaptcha::score()->threshold(0.7)->action('post-comment')->input('my_score_input');

重要

在检查操作名称时,请确保您的前端操作与中间件中预期的匹配。

绕过认证用户

有时您可能希望在存在认证用户的情况下绕过 reCAPTCHA 检查,或者在基于分数的挑战中自动将其视为“人类”,尤其是在重复操作或用户已经完成挑战的情况下(例如在登录时)。

要排除认证用户,可以使用 forGuests(),并根据需要指定保护者。

use App\Http\Controllers\CommentController;
use App\Http\Controllers\MessageController;
use DarkGhostHunter\Captcha\ReCaptcha;
use Illuminate\Support\Facades\Route

// Don't challenge users authenticated on the default (web) guard.
Route::post('message/send', [MessageController::class, 'send'])
     ->middleware(ReCaptcha::invisible()->forGuests());

// Don't challenge users authenticated on the "admin" and "moderator" guards.
Route::post('comment/store', [CommentController::class, 'store'])
     ->middleware(ReCaptcha::score(0.7)->action('comment.store')->forGuests('admin', 'moderator'));

然后,在您的 blade 文件中,您可以使用 @guest@auth 指令轻松跳过挑战。

<form id="comment" method="post">
    <textarea name="body"></textarea>

    @auth
        <button type="submit">Post comment</button>
    @else
        <button class="g-recaptcha" data-sitekey="{{ captchavel('invisible') }}" data-callback="onSubmit">
            Post comment
        </button>
    @endauth
</form>

伪造 reCAPTCHA 分数

您可以通过将 RECAPTCHA_FAKE 设置为 true 来轻松伪造本地开发中的 reCAPTCHA 响应分数。

RECAPTCHA_FAKE=true

此环境变量允许通过填写表单中的 is_robot 输入来伪造机器人响应。

<form id="comment" method="post">
    <textarea name="body"></textarea>

    @env('local')
        <input type="checkbox" name="is_robot" checked>
    @endenv

    <button class="g-recaptcha" data-sitekey="{{ captchavel('invisible') }}" data-callback='onSubmit'>
        Post comment
    </button>
</form>

确认中间件

ReCaptcha 附带了一个方便的中间件,通过简单的 reCAPTCHA 复选框挑战进行确认,就像 Laravel 中包含的 密码确认 中间件一样。

首先,使用 recaptcha.confirm 中间件设置要保护的路由。

use Illuminate\Support\Facades\Route;

Route::get('/settings', function () {
    // ...
})->middleware('recaptcha.confirm');

Route::post('/settings', function () {
    // ...
})->middleware('recaptcha.confirm');

完成操作后,确保您还有一个 recaptcha.confirm 路由来接收重定向的用户,以及一个接收挑战的路由,该路由与路径相同(如视图 POST 请求所做的那样)。ReCaptcha 附带了一个控制器和一个基本视图,您可以直接使用。

use Illuminate\Support\Facades\Route;
use Laragear\ReCaptcha\Http\Controllers\ConfirmationController;

Route::get('recaptcha', [ConfirmationController::class, 'show'])
    ->name('recaptcha.confirm');

Route::post('recaptcha', [ConfirmationController::class, 'confirm'])

当用户尝试进入路由时,它将被重定向到请求解决 reCAPTCHA 挑战的视图。完成后,它将被重定向到目标 URL。

img.png

该中间件与 记住挑战 兼容,并且如果全局启用,它将使用默认的时间量不再次请求,否则每次都会要求确认。

您可以在第一个参数之后配置路由名称和保护者,以便在用户认证后绕过确认。

use Illuminate\Support\Facades\Route;

Route::get('/settings', function () {
    // ...
})->middleware('recaptcha.confirm:my-custom-route,web,admin');

前端集成

请参阅 官方 reCAPTCHA 文档 以在您的前端或 Android 应用程序中集成 reCAPTCHA 脚本。

您可以使用 recaptcha() 助手根据您要渲染的挑战版本输出站点密钥:checkboxinvisibleandroidscore(v3)。

<form id='login' method="POST">
    <input type="email" name="email">
    <input type="password" name="password">

    <button class="g-recaptcha" data-sitekey="{{ recaptcha('invisible') }}" data-callback='onSubmit'>
        Login
    </button>
</form>

高级配置

ReCaptcha 旨在开箱即用,但您可以将配置文件发布以微调 reCAPTCHA 验证。

php artisan vendor:publish --provider="Laragear\ReCaptcha\ReCaptchaServiceProvider" --tag="config"

您将获得一个包含此数组的配置文件

<?php

return [
    'enable'            => env('RECAPTCHA_ENABLE', false),
    'fake'              => env('RECAPTCHA_FAKE', false),
    'hostname'          => env('RECAPTCHA_HOSTNAME'),
    'apk_package_name'  => env('RECAPTCHA_APK_PACKAGE_NAME'),
    'threshold'         => 0.5,
    'remember' => [
        'enabled' => false,
        'key'     => '_recaptcha',
        'minutes' => 10,
    ],
    'client' => [
        'version' => 2.0,
    ],
    'credentials'       => [
        // ...
    ]
];

启用开关

return [
    'enable' => env('RECAPTCHA_ENABLE', false),
];

默认情况下,ReCaptcha是禁用的,因此它不会检查reCAPTCHA挑战,在基于得分的路由上,它始终将其解析为人类交互。

您可以通过RECAPTCHA_ENABLE环境变量来启用它。

RECAPTCHA_ENABLE=true

在某些本地或开发环境中启用它可能很有用,以检查使用包含的localhost测试密钥的真实交互,这些密钥只能在localhost上工作。

重要

当关闭时,reCAPTCHA v2挑战不会在请求输入中进行验证,因此您可以安全地忽略任何前端脚本或reCAPTCHA令牌或框。

伪造响应

RECAPTCHA_FAKE=true

如果启用了ReCaptcha,将此设置为true将允许您的应用程序从reCAPTCHA服务器伪造v3-score响应。对于v2挑战,将此设置为true将绕过挑战验证。

您应该在运行单元测试时启用它。

警告

请记住在生产环境中禁用伪造。如果不这样做,将伪造所有得分挑战为人类,不需要挑战令牌。

主机名和APK包名

RECAPTCHA_HOSTNAME=myapp.com
RECAPTCHA_APK_PACKAGE_NAME=my.package.name

如果您在reCAPTCHA管理面板中未验证主机名或APK包名,可能是因为您使用多个主机名或应用程序,您必须在环境文件中指定它们。

当从服务器检索reCAPTCHA响应时,如果存在,将检查这些值。如果存在不匹配,将抛出验证异常。

阈值

return [
    'threshold' => 0.4
];

检查reCAPTCHA v3挑战的默认阈值。值等于或高于将被视为“人类”。

如果您不使用reCAPTCHA v3或您对此默认值满意,请勿更改。您仍然可以根据路由覆盖默认值

记住

return [
    'remember' => [
        'enabled' => false,
        'key'     => '_recaptcha',
        'minutes' => 10,
    ],
];

默认情况下,成功完成V2挑战后记住用户是禁用的。

如果您期望只有一些路由记住挑战,而不是整个应用程序,建议您按路由使用“记住”

这还控制了设置“记住”的分钟数。您可以将INF常量设置为永远记住挑战(或直到会话过期)。

HTTP客户端选项

return [
    'client' => [
        'version' => 3.0,
    ],
];

此数组设置对reCAPTCHA服务器的请求的选项。这是由Guzzle处理的,它反过来将它们传递给底层传输。根据您的系统,它可能是cURL。

默认情况下,它指示Guzzle尽可能使用HTTP/3。

凭据

return [
    'credentials' => [
        // ...
    ]
];

以下是根据版本使用完整reCAPTCHA凭据的数组。除非您知道自己在做什么,否则不要更改此数组。

使用ReCaptcha v2进行开发

在本地开发中,使用默认的测试密钥。这些密钥旨在用于本地开发,因此在生产环境中可以轻松地将它们更改为真实密钥。

在单元测试中,中间件将检测环境并跳过强制性的挑战检查。无需禁用ReCaptcha

使用ReCaptcha v3(得分)进行开发

在本地开发和单元测试中,中间件将自动创建人工响应。无需禁用ReCaptcha,但为了使用is_robot在实时请求中启用模拟机器人响应,启用模拟是强制性的。

在您的测试中,您可以通过简单地使用fakeHuman()fakeRobot()方法来模拟由人工或机器人发出的响应,这些方法将为后续请求分别评分1.00.0

<?php

use Laragear\ReCaptcha\Facades\ReCaptcha;

// Let the user login normally.
ReCaptcha::fakeHuman();

$this->post('login', [
    'email' => 'test@test.com',
    'password' => '123456',
])->assertRedirect('user.welcome');

// ... but if it's a robot, force him to use 2FA.
ReCaptcha::fakeRobot();

$this->post('login', [
    'email' => 'test@test.com',
    'password' => '123456',
])->assertViewIs('login.2fa');

注意

模拟响应不包含操作、主机名或APK包名,因此这些不会被验证。

或者,使用fakeScore()方法可以模拟您设置的任何分数的响应。

<?php

use Laragear\ReCaptcha\Facades\ReCaptcha;

// A human comment should be public.
ReCaptcha::fakeScore(0.8);

$this->post('comment', [
    'body' => 'This comment was made by a human',
])->assertSee('Your comment has been posted!');

// Moderate a post if there is no clear separation.
ReCaptcha::fakeScore(0.4);

$this->post('comment', [
    'body' => 'Comment made by something.',
])->assertSee('Your comment will be reviewed before publishing.');

// A robot shouldn't be able to comment.
ReCaptcha::fakeScore(0.2);

$this->post('comment', [
    'body' => 'Comment made by robot.',
])->assertSee('Robots are not welcomed here! Go away!');

PhpStorm存根

对于PhpStorm用户,有一个存根文件和一个元文件来帮助自动补全此包的宏。您可以使用phpstorm标签来发布它们。

php artisan vendor:publish --provider="Laragear\ReCaptcha\ReCaptchaServiceProvider" --tag="phpstorm"

该文件将发布到您项目的.stubs文件夹中,而元文件位于.phpstorm.meta.php目录中。您应该将PhpStorm指向这些存根。

Laravel Octane兼容性

  • 唯一注册的单例是使用过时配置实例的ReCaptcha类。您不应该更改配置
  • 在请求过程中没有写入静态属性。

使用Laravel Octane时,应无任何问题使用此包。

HTTP/3和cURL

要使用HTTP/3,请确保您正在使用PHP 8.2或更高版本确保您使用PHP 8.2或更高版本。cURL版本7.66支持HTTP/3,而最新的PHP 8.2使用版本7.85。

有关检查您的平台是否可以发出HTTP/3请求的更多信息,请查看此PHP Watch文章

安全性

如果您发现任何安全问题,请通过电子邮件darkghosthunter@gmail.com报告,而不是使用问题跟踪器。

许可

此特定包版本根据发布时的MIT许可证进行许可。

LaravelTaylor Otwell的商标。版权所有©2011-2024 Laravel LLC。