botguard / laravel
BotGuard 的 Laravel 适配:配置、Facade、表单验证规则 botguard:action、Blade 组件 <x-botguard>
Requires
- php: >=8.1
- botguard/php-sdk: ^0.1.1
- illuminate/container: ^9.0|^10.0|^11.0|^12.0|^13.0
- illuminate/contracts: ^9.0|^10.0|^11.0|^12.0|^13.0
- illuminate/support: ^9.0|^10.0|^11.0|^12.0|^13.0
- illuminate/validation: ^9.0|^10.0|^11.0|^12.0|^13.0
- illuminate/view: ^9.0|^10.0|^11.0|^12.0|^13.0
- psr/log: ^1.0|^2.0|^3.0
Requires (Dev)
None
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-10-22 15:05:49 UTC
README
在 Laravel 里用 BotGuard,写法对应 yihuipay 现在的 yidun_captcha()->verify()。支持 Laravel 9–13、PHP ≥ 8.1,依赖 botguard/php-sdk。
安装
composer require botguard/laravel
php artisan vendor:publish --tag=botguard-config # 可选,生成 config/botguard.php
ServiceProvider 和 BotGuard Facade 会被 Laravel 自动发现。在 .env 中配置:
BOTGUARD_ENDPOINT=https://botguard.example.com # 服务端调用 verify 的地址 BOTGUARD_PUBLIC_ENDPOINT=https://botguard.example.com # 浏览器加载组件的地址(通常相同) BOTGUARD_APP_KEY=app_xxx # botguard-ctl app create BOTGUARD_SECRET_ID=sid_xxx # botguard-ctl secret create BOTGUARD_SECRET_KEY=xxxxxxxx # 只放服务端,不要进视图或 JS
前端:Blade 组件
<form method="post" action="/login"> @csrf <input name="username"> <input name="password" type="password"> <x-botguard action="login" /> <button>登录</button> </form>
组件会从 gateway 加载脚本,用法类似易盾的 load.min.js,并在表单里放入隐藏字段 botguard_token。
如果想在点提交按钮时再弹出验证(类似易盾的 bind 模式),给出 bind 即可,默认就是弹窗:
<button id="login-btn">登录</button> <x-botguard action="login" bind="#login-btn" />
mode 写了就以 mode 为准,没写时:有 bind 为 popup,否则为 embed。几种组合:
| 写法 | 效果 |
|---|---|
<x-botguard action="login" /> |
嵌入式组件 |
bind="#btn" |
弹窗;点按钮先验证,通过后再提交表单 |
mode="embed" bind="#btn" |
嵌入式组件;点按钮时若还没验证,先完成验证再提交 |
mode="popup"(不写 bind) |
弹窗组件,由你自己的 JS 调用元素的 verify() 打开 |
mode 只能是 embed 或 popup,写错会在渲染时报错。
H5 页面或 App 内嵌页面,可以压缩高度、改主色:
<x-botguard action="login" size="compact" width="100%" image-height="120" accent="#ff6a00" />
其他属性(class、id、data-*、auto 等)会原样加到 <botguard-widget> 元素上;bind 模式下由脚本设置到生成的元素上。endpoint、app-key、field-name 只取自配置,不能通过属性覆盖。
后端:三种写法
// 1. 表单验证规则:先校验普通字段,通过后再单独校验 botguard $data = $request->validate([ 'username' => 'required', 'password' => 'required', ]); $request->validate(['botguard_token' => 'botguard:login']); // 或 ['botguard_token' => [new \BotGuard\Laravel\Rules\BotGuardToken('login')]] // 2. 布尔值,等同易盾的写法 if (!botguard()->passes($request->input('botguard_token'), 'login')) { return back()->withErrors(['botguard_token' => '验证失败']); } // 3. 完整结果,可区分各种情况,并读取风险信息 $result = \BotGuard::verify($request->input('botguard_token'), 'login'); if ($result->passed() && $result->riskLevel === 'high') { // 业务自行决定是否追加短信验证 }
额外验证(step-up)
有三类 token 网关判为通过,但拿到的成本对机器人很低。passes() 和两种验证规则默认把它们当作未通过:
| 原因码 | 情况 | 放行开关(.env) |
|---|---|---|
STEP_UP_FALLBACK |
用户选了无障碍备用路径,只有工作量证明 | BOTGUARD_ACCEPT_FALLBACK=true |
STEP_UP_DEGRADED |
网关图片题容量不足,降级为工作量证明(攻击者可以故意刷满配额触发) | BOTGUARD_ACCEPT_DEGRADED=true |
STEP_UP_HIGH_RISK |
风险等级为高,解出了最难的题但很可能是自动化 | BOTGUARD_ACCEPT_HIGH_RISK=true |
被拦下时,botguard()->lastStepUp() 返回原因码,可以改为让用户做短信验证等额外检查,而不是再弹一次验证码;BotGuardToken 规则的错误信息会变成「需要进一步验证」。verify() 不做这项判断,原样返回网关的结果,\BotGuard\Laravel\BotGuard::stepUp($result) 可以自己判断。
if (!botguard()->passes($request->input('botguard_token'), 'login')) { if (botguard()->lastStepUp() !== null) { return redirect()->route('login.sms'); // 通过了验证码,但还要再验证一次 } return back()->withErrors(['botguard_token' => '验证失败']); }
限定来源
应用在 gateway 里的允许来源用了通配(https://*.example.com)或 * 时,别的站点拿到的 token 也会通过。在 .env 里列出自己的页面来源,passes() 和验证规则只放行从这些来源拿到的 token:
BOTGUARD_ORIGINS=https://shop.example.com,https://*.example.com
通配只匹配子域名,不含 example.com 本身,规则与 gateway 的允许来源一致。名单外的来源、以及旧版 gateway 不返回来源的 token 都按未通过处理(记 info 日志);名单里有格式错误的项时拒绝所有 token 并记 error 日志。BotGuard::originAllowed($patterns, $result->origin) 可以在 verify() 之后自己判断。需要 botguard/laravel 0.3.0 及以上。
token 什么时候被消耗
规则一执行就会消耗 token。Laravel 在某个字段校验失败后,仍会继续校验其余字段(bail 只对同一个字段内的多条规则生效),所以把 botguard 规则写在数组最后并不能避免消耗:用户名写错时 token 照样被用掉,用户改完再提交只会得到“验证未通过”。两种做法:
- 推荐:像上面那样分两步,普通字段全部通过后再单独
validatebotguard。 - 或者在同一个校验器里把 botguard 放在最后,并开启“首个失败即停止”:
Validator::make($request->all(), [ 'username' => 'required', 'password' => 'required', 'botguard_token' => 'botguard:login', // 必须放最后 ])->stopOnFirstFailure()->validate(); // FormRequest 中:protected $stopOnFirstFailure = true;
规则说明
botguard规则和BotGuardToken都是隐式规则:字段缺失、为空字符串或 null 时同样执行并判为失败,不写required、写了nullable也不会被跳过(显式写sometimes时,字段缺失则按 Laravel 语义跳过,请勿对验证码字段使用)。botguard规则必须且只能带一个 action 参数:botguard、botguard:、botguard:a,b都会抛InvalidArgumentException。new BotGuardToken('')同样抛出。- action 必须由后端写死,不能取自浏览器提交的值;格式为
[a-z0-9_]、1–32 个字符,格式不对时不会发请求,verify()抛CallerErrorException(INVALID_REQUEST),passes()和规则返回 false 并写错误日志。
说明:
passes()、verify()接收浏览器提交的任意值:null、数组、非字符串、非法 UTF-8、超长或含非法字符的 token 一律在本地判为拒绝(reasonTOKEN_INVALID,与网关一致),不发请求,也不会抛TypeError/JsonException导致 500。passes()和验证规则遇到配置错误、服务不可用时也返回 false(不放行),但会写一条 error 日志,以免被误当成“用户没通过”。- 未配置
BOTGUARD_APP_KEY/BOTGUARD_SECRET_ID/BOTGUARD_SECRET_KEY时,应用照常启动、页面照常渲染;passes()和规则返回 false 并写 error 日志,verify()抛CallerErrorException(errorCode为NOT_CONFIGURED)。 - 同一个请求里,同一个 token 和 action 只会核验一次,所以验证规则被执行两次也不会把 token 消耗掉。
BOTGUARD_TIMEOUT(默认 3 秒)和BOTGUARD_CONNECT_TIMEOUT(默认 1 秒)为空、0、负数或非数字时使用默认值,小于 0.05 秒按 0.05 秒算,不会出现“不限时”把 PHP-FPM 进程挂住的情况。- 规则、
botguard()helper 和 Facade 每次都从当前容器取服务,Octane 下不会拿到启动时的旧容器。