应直接使用spatie/laravel-permission而非手写权限系统,因其上线后修改成本高;需正确处理迁移、模型配置、角色权限初始化及guard上下文一致性。

直接用 spatie/laravel-permission,别手写五张表+中间件+策略组合——它不是“不能做”,而是上线后改权限逻辑、加 guard、清缓存、查错成本高到不值得。
安装与迁移必须一次性跑通
很多报错卡在 php artisan migrate 失败,本质是 MySQL 版本低或字符集没对齐:
- 如果看到
Specified key was too long错误,必须在AppServiceProvider::boot()里加\Schema::defaultStringLength(191) - 别跳过
php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider" --tag="migrations"—— 这步生成的迁移文件含model_has_permissions等五张表,不是可选 -
php artisan migrate后检查数据库:确认roles、permissions、model_has_roles、model_has_permissions、role_has_permissions全部存在且字段完整
User 模型必须引入 HasRoles trait
只改 User 类还不够,常见漏点:
- trait 要放在
Authenticatable之后,否则can()、hasRole()方法不可用 - 确保
use Spatie\Permission\Traits\HasRoles;在类开头,且use HasRoles;写在class User extends Authenticatable的花括号内 - 如果用了自定义用户模型路径(比如
app/Models/User.php),别忘了更新config/auth.php中的'providers.users.model'指向正确类
角色与权限分配不能靠硬编码塞进控制器
开发期用 php artisan tinker 或 Seeder 初始化更安全:
- 别在
TestController@test里反复调Role::create()—— 多次执行会触发Integrity constraint violation(name 唯一) - 权限名建议用点分隔(如
post.edit、user.delete),避免空格或斜杠,否则@can('post edit')会失效 - 给用户分配角色用
$user->assignRole('admin');直接授予权限用$user->givePermissionTo('post.edit')—— 二者不互斥,但优先走角色继承,便于批量调整
中间件和 Blade 判断要匹配 guard
默认 guard 是 web,但后台可能用 admin guard,不显式指定就会 403:
- 路由中间件写成
middleware('permission:post.edit')时,它只查webguard 下的权限;若用户用adminguard 登录,得写成middleware('permission:post.edit,admin') - Blade 里
@can('post.edit')同样默认走web,要改成@can('post.edit', \Auth::guard('admin')->user()) - 缓存权限关系后,改完角色/权限记得执行
php artisan permission:cache-reset,否则@can还是旧结果
最常被忽略的是 guard 上下文一致性——同一用户在不同 guard 下权限完全隔离,连 role_has_permissions 表里的记录都按 guard_name 字段区分,这点不厘清,查半天发现权限“丢了”,其实是查错了 guard 分区。


















