昨天在设定完身分验证的架构後已经可以在路由上加 auth 中介层来验证使用者的身分是否允许访问。
Route::get('/flights', function () {
// 通过 admin 检查的用户才能进入路由
})->middleware('auth:admin');
不过除了只看身分外,Laravel 可以设定更详细的权限检查,像是检查使用者是否拥有这笔 Todo ,是的话才能进行删除等等。
定义权限的方式分为 Guard 跟 Policy ,两者相较下 Policy 的架构会比较好管理跟维护,所以这里介绍的重点会放在 Policy 上,Guard 就只稍微介绍点,不过其实 Policy 也是 Guard 的应用就是。
Guard 就像路由,一笔笔的定义某个行径对应的检查函式,一般都定义在 AuthServiceProvider 中。
/app/Providers/AuthServiceProvider.php
use App\Models\Todo;
use App\Models\User;
use Illuminate\Support\Facades\Gate;
public function boot()
{
$this->registerPolicies();
Gate::define('update-todo', function (User $user, Todo $todo) {
return $user->id === $todo->user_id;
});
}
进行 update-todo 检查时,会检查传入的 user 跟 todo ,若 todo 是属於 user 的则回传 true 表示权限许可,否则为 false。
use Illuminate\Support\Facades\Gate;
public function update(Request $request, Todo $todo)
{
if (! Gate::allows('update-todo', $todo)) {
abort(403);
}
// continue update process ...
}
接着就能用 Gate::allows 进行检查,这边要注意我们并没有传入 $user 参数,因为 Gate Facade 会自动从 Auth 取得目前登入的用户代入。
如果想验证的不是目前登入的使用者,加上 forUser 方法指定要验证的使用者
if (! Gate::forUser($user)->allows('update-todo', $todo)) {
abort(403);
}
Policy 用於制定使用者对於某个 Model 的操作权限,其本质就是一组针对特定 Model 的 Gate 。
如果用指令创建 Model 的时候选择了全餐 -a ,当中就会包含对应的 Policy ,如果没有的话,就另外建立。
sail artisan make:policy TodoPolicy --model Todo
如果在建立 Policy 时有指定 Model 的话,就会预先产生一些方法像是 view,create,update 等。
Policy 产生後要在 AuthServiceProvider 中登记对特定 Model 进行权限检查时要用哪个 Policy 。
/app/Providers/.php
<?php
namespace App\Providers;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Auth;
class AuthServiceProvider extends ServiceProvider
{
protected $policies = [
App\Models\Todo::class => App\Policies\TodoPolicy::class,
];
//...
}
除了这里之外也能动态的用 Gate::policy() 定义,例如:
$user = Auth::user();
if($user instanceof User){
\Gate::policy( Todo::class, TodoUserPolicy::class);
}else if($user instanceof Admin){
\Gate::policy( Todo::class, TodoAdminPolicy::class);
}
如果有复数身分 Model 可以考虑这麽做,不过一般都建议不要用多个身分验证模型提高复杂度。
如果完全没有注册 Model 对应的 Policy, Guard 会试图自动找出对应的 Policy ,寻找步骤如下:
可以看到用指令产生的 Policy 都符合上述的步骤,所以一般其实不用特地到 AuthServiceProvider 登记 Policy。
就跟写 Guard 的 callback 函式相同,第一个参数传入 User ,之後看要传入哪个 Model。
public function update(User $user, Todo $todo)
{
return $user->id === $todo->user_id;
}
$user 要视验证的身分标记对应的 Model ,一般来说是 User ,不过如果 Policy 用来对应 Admin 模型的验证的话就要改成 Admin。
public function update(Admin $admin, Todo $todo)
{
return $admin->id === $todo->user_id;
}
权限检查除了回传 true / false 以外,也可以回传带有更多资讯的 Response 物件。注意是用 Auth 的 Response 。
use Illuminate\Auth\Access\Response;
public function update(User $user, Todo $todo)
{
return $user->id === $todo->user_id
? Response::allow()
: Response::deny('You do not own this post.');
}
回传 Response 後如果用 can , allows 等方法,回传还是一般的 true / false,要看到 Response 的内容的话要用 Gate 的 inspect 方法。
$response = Gate::inspect('update', $todo);
if ($response->allowed()) {
// The action is authorized...
} else {
echo $response->message();
}
Policy 中有特别的函式 before ,顾名思义会在进行任何检查前执行,如果 before 回传 null 以外的值的话该值会被当成这次检查的回传值。
public function before(User $user, $ability)
{
// 如果用户是 admin 就跳过检查一律回传 true
if ($user->isAdministrator()) {
return true;
}
}
写好之後终於可以用啦,用法跟 Guard 相似不过是从 User 出发进行检查,并且不是用 allows 而是 can 跟 cannot 等方法检查。
allows/ denies/ inspect 是 Guard 类别的方法
can/ cannot 是 Authorizable 类别(User 继承的类别)的方法
注意别用错了
if ($request->user()->cannot('update', $todo)) {
abort(403);
}
另外当验证的方法不用除了 User 以外的资料时,需要带入 Model 名称才知道要找哪个 Policy 进行验证。
if ($request->user()->cannot('create', Todo::class)) {
abort(403);
}
<<: 【Side Project】 (老板)订单清单UX功能实作2-ChcekBox
昨天我们已经将 gotop按钮实做出来 但有时候我们不想要它一直出现 而是使用者滚轮滑到下面 它才会...
1.display显示模式设定:可控制 HTML 标签的显示模式,主要分为 block 与 inli...
特徵工程是机械学习相当重要的一环,有处理数据以及实行 ML/DL 任务经验者对特徵工程一定不陌生,...
继续介绍昨天主流程里的副程序吧。 今日要点: 》Google Docs 转换 API Bluepr...
昨天成功的用terraform创建gcp的instance出来,也透过terraform自动的把in...