Laravel 11:使用Gates和Policies限制用户访问Sites资源
解决Laravel 11中限制用户仅查看自身Site资源的问题
问题分析
你的代码存在几个关键问题:
AppServiceProvider中的guessPolicyNamesUsing闭包为空,导致Laravel无法正确关联Site模型和SitePolicy- 路由的
can中间件传递的是字符串'site',而非实际的Site实例,策略无法获取到要检查的资源 SitePolicy的view方法没有实现实际的权限判断逻辑- 路由的控制器写法不符合Laravel 11的规范(应指定控制器类和方法)
解决方案
1. 修复策略与模型的关联
Laravel默认会按照「模型名+Policy」的约定自动关联模型和策略,直接删除AppServiceProvider中guessPolicyNamesUsing的空闭包即可:
namespace App\Providers; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider { public function register(): void { } public function boot(): void { // 移除空的guessPolicyNamesUsing,依赖框架默认约定 } }
如果需要自定义策略命名规则,需在闭包中返回正确的策略类名:
use Illuminate\Support\Facades\Gate; Gate::guessPolicyNamesUsing(function (string $modelClass) { return 'App\\Policies\\' . class_basename($modelClass) . 'Policy'; });
2. 修正路由配置
使用模型绑定让Laravel自动根据id获取Site实例,同时在can中间件中传递该实例:
use App\Http\Controllers\SiteController; use App\Models\Site; Route::get('/view/{site}', [SiteController::class, 'view']) ->name('sites.view') ->can('view', 'site'); // 这里的'site'对应路由参数{site}的名称
若无法使用模型绑定,也可通过闭包传递实例:
Route::get('/view/{id}', [SiteController::class, 'view']) ->name('sites.view') ->can('view', function ($id) { return \App\Models\Site::findOrFail($id); });
3. 完善SitePolicy的权限判断逻辑
假设Site模型通过user_id字段关联用户,修改view方法判断当前用户是否为站点所有者:
namespace App\Policies; use App\Models\Site; use App\Models\User; class SitePolicy { public function view(User $user, Site $site): bool { // 验证用户ID与站点的user_id匹配 return $user->id === $site->user_id; } }
如果是多对多关联(用户与站点通过中间表绑定),可改为:
public function view(User $user, Site $site): bool { return $user->sites()->where('site_id', $site->id)->exists(); }
4. 调整控制器方法接收Site实例
在SiteController的view方法中直接接收经过权限验证的Site实例:
namespace App\Http\Controllers; use App\Models\Site; class SiteController extends Controller { public function view(Site $site) { // 直接使用已验证的$site数据返回视图或响应 return view('sites.view', compact('site')); } }
测试验证
访问/view/{site_id}时,非站点所有者会收到403禁止访问响应,所有者则可正常查看站点内容。
内容的提问来源于stack exchange,提问作者Dev Newbie
相关产品推荐
相关产品推荐

