本文基于源代码(1.x 分支)整理。目前尚无官方文档,是正式发布前的包(截至 2026 年 4 月)。
什么是 Sentinel
Laravel Sentinel 是一个基于驱动、控制路由访问的安全中间件包。由 Taylor Otwell 和 Mior Muhammad Zaki 开发,支持 PHP ^8.0 / Laravel 8~13。
主要用途是保护 Telescope、Horizon、Pulse 等管理工具类路由。只要给路由挂上 SentinelMiddleware,就能用驱动定义的授权逻辑控制访问。
与传统做法的对比
Telescope 和 Horizon 各自都提供了基于 gate 的授权机制。
// 传统做法(TelescopeServiceProvider 等)
Gate::before(function ($user) {
return $user->isAdmin() ? true : null;
});
这种方式依赖用户的认证状态,存在“不登录就无法判定”的问题。Sentinel 作为中间件运行,无论是否已认证,都可以按请求维度控制访问。同时可以在一处定义规则,应用于多个管理工具。
composer require laravel/sentinel
composer.json 的 extra.laravel.providers 中已经注册了 SentinelServiceProvider,服务提供者会被自动发现。也不需要额外发布配置文件。
包的架构
默认驱动(Laravel 驱动)的行为
默认的 Laravel 驱动**只在本地环境(APP_ENV=local)**才会做控制。
// src/Drivers/Laravel.php
public function authorize(Request $request): bool
{
if (! $this->app()->environment('local')) {
return true; // 非 local 一律放行
}
// 通过隧道服务访问时给出警告
if ($this->isPrivateIp($request->ip())
&& ! $request->isFromTrustedProxy()
&& Str::endsWith($request->host(), ['.sharedwithexpose.com', '.ngrok-free.app', '.ngrok.io'])) {
throw new RuntimeException(
'Unable to access "..." using "local" environment, ...'
);
}
// Docker 本地环境放行
if ($this->isRunningOnDockerLocally($request)) {
return true;
}
// 检查是否经反向代理访问
return $this->authorizeAccessingViaReverseProxies($request);
}
流程总结如下:
Laravel 驱动在非 local 环境总是返回 true(允许)。若需要在生产环境做访问控制,请自定义驱动。
私有 IP 判定
Driver 基类的 isPrivateIp() 使用 Symfony 的 IpUtils,把以下 IP 范围视为私有 IP。
| 范围 | 说明 |
|---|
127.0.0.0/8 | 回环(RFC1700) |
10.0.0.0/8 | 私有(RFC1918) |
192.168.0.0/16 | 私有(RFC1918) |
172.16.0.0/12 | 私有(RFC1918) |
169.254.0.0/16 | 链路本地(RFC3927) |
::1/128 | IPv6 回环 |
fc00::/7 | IPv6 唯一本地 |
fe80::/10 | IPv6 链路本地 |
Docker 本地环境检测
// src/Drivers/Driver.php
protected function isRunningOnDockerLocally(Request $request): bool
{
return $request->server->get('REMOTE_ADDR') === '127.0.0.1'
&& file_exists(base_path('.dockerenv'));
}
当 REMOTE_ADDR 是 127.0.0.1,且项目根目录存在 .dockerenv 文件时,认定为 Docker 本地环境。
作为中间件使用
基本用法
use Laravel\Sentinel\Http\Middleware\SentinelMiddleware;
Route::middleware(SentinelMiddleware::class)->group(function () {
Route::get('/telescope', function () { /* ... */ });
Route::get('/horizon', function () { /* ... */ });
Route::get('/pulse', function () { /* ... */ });
});
指定驱动
可以通过中间件参数传入驱动名。如果传入不存在的驱动名,会回退到默认驱动(driverOrFallback() 的行为)。
Route::middleware([SentinelMiddleware::class . ':custom-driver'])->group(function () {
// 使用自定义驱动做授权
});
中间件实现
// src/Http/Middleware/SentinelMiddleware.php
public function handle(Request $request, Closure $next, ?string $driver = null)
{
abort_unless(Sentinel::driverOrFallback($driver)->authorize($request), 401);
return $next($request);
}
authorize() 返回 false 时会中断并返回 HTTP 401。使用 Driver::authorizeOrFail() 时也可以抛 AuthorizationException(不过中间件本身没走这个分支)。
编写自定义驱动
继承 Driver 抽象类并实现 authorize() 方法,就能写出自定义驱动。
use Laravel\Sentinel\Sentinel;
use Laravel\Sentinel\Drivers\Driver;
use Illuminate\Http\Request;
Sentinel::extend('admin-only', function ($app) {
return new class extends Driver {
public function authorize(Request $request): bool
{
// 只允许已认证且拥有 admin 角色的用户
$user = $request->user();
return $user !== null && $user->hasRole('admin');
}
};
});
extend() 的注册可以放在 AppServiceProvider::boot() 里。
利用基类的实用方法
Driver 类里内置了 IP 判定等实用方法,自定义驱动可以直接使用。
public function authorize(Request $request): bool
{
// 生产环境只允许来自私有 IP 的请求
if ($this->app()->environment('production')) {
return $this->isPrivateIp($request->ip());
}
// 本地只允许 Docker 或直接访问
return $this->isRunningOnDockerLocally($request)
|| $this->authorizeAccessingViaReverseProxies($request);
}
SentinelManager 的机制
SentinelManager 继承自 Illuminate\Support\Manager。Manager 是 Laravel 用来实现驱动模式的基础类,driver() 会缓存并解析驱动实例。
// src/SentinelManager.php
class SentinelManager extends Manager
{
protected function createLaravelDriver()
{
return new Laravel(fn () => $this->getContainer());
}
public function getDefaultDriver()
{
return 'laravel';
}
public function driverOrFallback(?string $driver)
{
return rescue(
fn () => $this->driver($driver),
value(fn () => $this->driver()),
false // 不记录日志
);
}
}
driverOrFallback() 在找不到指定驱动时会静默回退到默认驱动,即便你给中间件参数传入不存在的驱动名,也不会报错。
SentinelManager 被 SentinelServiceProvider 注册为作用域单例(scoped),因此在同一次请求中会复用驱动实例。
// src/SentinelServiceProvider.php
public function register(): void
{
$this->app->scoped(SentinelManager::class, fn ($app) => new SentinelManager($app));
}
laravel/sentinel 虽然是一个小包,但通过基于 Illuminate\Support\Manager 的驱动模式,具备很高的可扩展性。
| 项目 | 内容 |
|---|
| 版本 | 1.x |
| 支持 PHP | ^8.0 |
| 支持 Laravel | 8~13 |
| 默认驱动 | 仅在 local 环境做控制 |
| 自定义驱动 | 通过 Sentinel::extend() 添加 |
默认的 Laravel 驱动专门用来防止本地开发时通过隧道服务的意外访问,生产环境的保护需要你自己写自定义驱动。等官方文档完善后,应该会有更具体的使用模式。
laravel/sentinel 仓库
源代码和最新变更可以在 GitHub 的 1.x 分支查看。