https://laravel.com/docs/5.8/authorization#writing-gates
使用者的操作權限,之前都是用Policy,沒用過Gate,覺得比較難理解。用Policy的前提是有一個對應的Model(文件裡又稱為resource),檢查使用者對這個Model有沒操作權限。
Gate寫在Closure裡比較有彈性,可以像Policy的判斷,也可以自定義,不用綁Model,例如超級管理員的權限檢查跟任何Model都沒關係。
Gate的定義:一般放在AuthServiceProvider,或要新增另一個Provider放也可以。
// 定義
// AppServiceProvider.php
public function boot()
{
Gate::define('update-post', function ($user, $post) {
// 條件
return $user->id === $post->user_id;
});
// 其他 Gate 定義
// Gate::define(...
// Gate::define(...
Gate::define('delete-post', function ($user) {
// 條件
return $user->is_admin;
});
}
Provider裡頭定義所有的Gate是全域型的,任何地方都可以使用來檢查權限。底下以allows與denies檢查權限。
allows:是否有該操作權限?
底下的code涵義為當前使用者對這一筆$post是否有更新權限,則要看上方「udpate-post」裡的匿名函數返回true或false。
// web.php
Route::get('/', function () {
$post = \App\Post::find(1);
dd(\Gate::allows('update-post', $post)); // return boolean
// 同上
// dd($user->can('update-post', $post));
});
denies:將allows的結果反轉,亦即當前使用者是否沒有操作權限。
假設當前使用者並不是管理者,allows返回false,不允許刪除的操作;denies返回true,否定刪除操作 (這裡譨腦要轉一下)。所以都是不可以刪除的意思。
// web.php
Route::get('/', function () {
dd(\Gate::allows('delete-post')); // return false
dd(\Gate::denies('delete-post')); // return true
// 同上
// dd($user->can('delete-post'));
// dd($user->cant('delete-post'));
});
codecourse筆記,一次性把多個權限檢查加入Gate定義中。
Permission Model:所有權限的名稱 (update-post, delete-post, …)
users_permissions:紀錄User與Permission多對多的關聯(pivot table)。
// AppServiceProvider.php
public function boot()
{
Permission::get()->map(function ($permission) {
Gate::define($permission->name, function ($user) {
return $user->hasPermissionTo($permission);
});
});
}
先不管code當中的return $user->…,map()的動作只是把每一個permission的名稱與Gate Closure綁在一起,跟前面例子一行一行跟Gate綁定是一樣的。
再來看看真的要檢查權限時,laravel怎麼運作?例如,$user->can(‘create-post’),laravel就會找上述Gate::define(‘create-post’, function () { … }),的Closure檢查權限。Closure檢查權限的部分是呼叫hasPermissionTo(例如帶入的是create-post物件)。
// User.php
public function hasPermissionTo($permission)
{
return $this->hasPermission($permission);
}
public function hasPermission($permission)
{
// 當前使用者的所有權限撈出來 (collection)
// 判斷$permission是否有在這其中 (有的話, count() = 1)
// 再轉成布林值返回
return (bool) $this->permissions->where('name', $permission->name)->count();
}
public function permissions()
{
return $this->belongsToMany(Permission::class, 'users_permissions');
}
以$user->can(‘create-post’)來說,當返回true就表示使用者有這個權限(users_permission裡頭有create-post),而返回false表示沒這個權限(users_permission裡頭沒有create-post)。
真的很難理解,應該是Closure的關係 = =。