Dcat Admin 使用 Laravel 的拦截器 Gate 和策略 Policy,能否对除 App\Models\User 模型以外的其它认证模型进行授权呢?
运行环境
- Valet 集成环境
当前使用的 Laravel 版本?
- Laravel Framework 9.33.0
- Dcat Admin version 2.2.2-beta
当前使用的 php/php-fpm 版本?
- PHP 版本:PHP 8.1.20 (cli) (built: Jun 22 2023 06:14:35) (NTS)
- php-fpm 版本:/usr/local/etc/php/8.1/php-fpm.conf
当前系统
- MacOs
问题描述?
- 最近使用
Dcat Admin
后台框架搭建了一个项目:后台有通用的
RBAC
管理模块,包括管理员、角色、权限以及角色和权限分配等相关内容。使用还比较很方便,但是最近漏扫发现了一个问题: 普通管理员,通过修改路由链接参数值,能够进入超级管理员信息的xxx/admin/auth/users/{参数id}/edit
编辑页面,然后进行密码修改获得超管账号
- 我就联想到
Laravel
中的Gate
和Policy
可以帮助我实现这个功能:它很安全,即使用户已经通过了「身份验证(authentication)」, 用户也可能无权对应用程序中重要的模型或数据库记录进行删除或更改。简单、条理化的系统性,是 Laravel 对授权管理的特性。
首先我使用 Policy
:
创建
policy
类php artisan make:policy AdminPolicy --model=Dcat\Admin\Admin
AdminPolicy
类内容<?php namespace App\Policies; use App\Models\Admin\AdminUser; use Dcat\Admin\Models\Administrator; use Dcat\Admin\Models\Role; use Illuminate\Auth\Access\HandlesAuthorization; use Illuminate\Auth\Access\Response; class AdminPolicy { use HandlesAuthorization; /** * Determine whether the user can view any models. * * @param Administrator $user * @return Response|bool */ public function viewAny(Administrator $user): Response|bool { return $user->isAdministrator(); } /** * Determine whether the user can view the model. * * @param Administrator $user * @param AdminUser $adminUser * @return Response|bool */ public function view(Administrator $user, AdminUser $adminUser): Response|bool { return intval($user->getAttribute('id')) === intval($adminUser->getAttribute('id')); } /** * Determine whether the user can create models. * * @param Administrator $user * @return Response|bool */ public function create(Administrator $user): Response|bool { return $user->isRole(Role::ADMINISTRATOR) || $user->isRole('manager'); } /** * Determine whether the user can update the model. * * @param Administrator $user * @param AdminUser $adminUser * @return Response|bool */ public function update(Administrator $user, AdminUser $adminUser): Response|bool { return intval($user->getAttribute('id')) === intval($adminUser->getAttribute('id')); } /** * Determine whether the user can delete the model. * * @param Administrator $user * @param AdminUser $adminUser * @return Response|bool */ public function delete(Administrator $user, AdminUser $adminUser): Response|bool { return intval($user->getAttribute('id')) === intval($adminUser->getAttribute('id')); } /** * Determine whether the user can restore the model. * * @param Administrator $user * @param AdminUser $adminUser * @return Response|bool */ public function restore(Administrator $user, AdminUser $adminUser): Response|bool { return intval($user->getAttribute('id')) === intval($adminUser->getAttribute('id')); } /** * Determine whether the user can permanently delete the model. * * @param Administrator $user * @param AdminUser $adminUser * @return Response|bool */ public function forceDelete(Administrator $user, AdminUser $adminUser): Response|bool { return intval($user->getAttribute('id')) === intval($adminUser->getAttribute('id')); } }
在
AuthServiceProvider
提供者中注册polic
类protected $policies = [ AdminUser::class => AdminPolicy::class, ];
在控制器中使用
public function edit($id, Content $content): Content { $adminUser = AdminUserModel::query()->find($id); $response = Gate::inspect('view', $adminUser); // 第一种:打印结果第一个为 false ,第二个为 null // 无论是自己的信息,还是不是自己的信息,都返回 false dd($response->allowed(), $response->message()); // 第二种:直接返回 403 权限验证失败页面 Gate::authorize('view', AdminUserModel::query()->find($id)); }
然后我使用 Gate
:
AuthServiceProvider
中boot
方法注册方法Gate::define('admin-view', function (Administrator $user, AdminUser $adminUser) { return $user->getAttribute('id') === $adminUser->getAttribute('id'); });
控制器中验证
public function edit($id, Content $content): Content { $adminUser = AdminUserModel::query()->find($id); $status = Gate::allows('admin-view', $adminUser); // 打印结果一直为 false dd($status); if (! $status) { abort(403); } }
您期望得到的结果?
Policy
和 Gate
能够验证,除 App\Models\User
以外的用户认证模型:比如说后台 App\Models\Admin\AdminUser
模型
您实际得到的结果?
最终验证不了, Policy
和 Gate
方法默认的第一个参数,就是 认证用户
,这个认证用户没搞明白是哪个模型的认证用户,所以得不出结果。
哈哈,抱歉,我还以为是分享贴。
你这个问题不在
Gate
方法,而在于获取当前 登录 用户时发生的问题,且听我娓娓道来:Dcat-admin
有自己的授权表和授权模型,并没有和User
模型混到一起,所以我们首先应该为Dcat-admin
添加一个guard
。打开
auth.php
config 文件我这里复制了
Dcat-admin
的模型文件到自己的项目,并继承它。然后我们需要修改一下
admin.php
配置文件,130行auth
将 Providers 的模型改成我们自己的,还有下面的 283 行
database
,更改默认的用户模型你上面做的都没问题,注册 Policy,定义 Gate 方法都是正确的。
那么为什么返回 false ?
不论是
Gate::allows
还是Gate::inspect
,他们都需要一个共同的东西,就是 当前授权用户,我们打开就如我上面所说,如果
Gate
方法获取不到当前登录用户,那么它肯定是无法判断权限的对吧?那么
Gate
为什么获取不到当前用户?我们打开auth.php
config 文件第 16 行这里设置了默认的用户组是
Web
,也就是默认的 User 模型,Gate 当然是来找这个模型相关的授权用户,但是Dcat-admin
有自己独立的一套授权系统,所以就产生了你上面不论怎么写都是返回 null。到此,问题已经明确,下面我们来修复它:
刚刚我们看到了 resoverUser() 方法是获取当前用户的,那么我们可以在调用 Gate 方法的时候告诉它当前登录用户是谁
Gate::forUser(Auth::guard('admin')->user())
我们告诉 Gate,不要去默认的 web 授权组去找,而是我们直接指定一个授权用户给它,让它去验证。希望能解决你的问题哈哈