OrzLee

这个世界上只有一个问题
那就是时间问题

laravel nova 权限管理工具

laravel-nova.png

前言

前几个月一直在折腾laravel nova,不得不说它的扩展性非常强。laravel nova packages每天都会有很多新鲜的扩展包,生态很好。spatie laravel-permission是非常不错的Role-based access control RBAC扩展包,orzlee在laravel 5.3就在使用了,这里主要说说spatie laravel-permissionlaravel nova权限管理工具。

Larval nova packages

目前有三款laravel nova packages包是基于spatie laravel-permission开发的,功能上大同小异,各有特色。

1. vyuldashev/nova-permission:

这是最早出现基于spatie laravel-permissionlaravel nova packages。支持自定义资源,本地化支持。

orzlee一开始也是使用的这款扩展包,但是没几天我就发现了一个问题,因为laravel novaRelationships并不会调用spatie laravel-permission自带的权限或角色附加、分离方法,所以在给用户添加权限时不会触发权限缓存更新。这就必须等待缓存过期或者手动清理缓存。

这个问题一直没有解决,orzlee也关注了Issues:Cache not invalidated on attaching a role or permission to user好长一段时间,然而并没有进展。最后还是提问者重新创建了一个新的包insenseanalytics/laravel-nova-permission

2. insenseanalytics/laravel-nova-permission:

这款包主要是使用ForgetCachedPermissions中间件来判断当前路由是否为附加或分离请求(attach,detach),然后过滤出附加或分离权限的请求,执行缓存清除。

分离时没有问题,但是附加在路由判断上有点问题。orzlee在测试的时候发现附加怎么也无法清除缓存,经过调试发现url没有匹配到。

class ForgetCachedPermissions
{
    /**
     * Handle the incoming request.
     *
     * @param \Illuminate\Http\Request $request
     * @param \Closure                 $next
     *
     * @return \Illuminate\Http\Response
     */
    public function handle($request, $next)
    {
        $response = $next($request);

        if ($request->is('nova-api/*/detach') ||
            $request->is('nova-api/*/*/attach/*')) {
            ##作者判断了attach,但是orzlee的请求路由为:http://127.0.0.1:8000/nova-api/admins/1/attach-morphed/permissions
            $permissionKey = (Nova::resourceForModel(app(PermissionRegistrar::class)->getPermissionClass()))::uriKey();

            if ($request->viaRelationship === $permissionKey) {
                app(PermissionRegistrar::class)->forgetCachedPermissions();
            }
        }

        return $response;
    }
}

orzlee已经向作者提交Issues了。

orzlee在给作者RP包语言本地化时有一个权限搜索翻译的功能没有被作者合并。作者觉得使用自定义资源功能让用户自行实现,这样也不错,不过度化设计。

权限是由开发者添加,而使用的时候基本上是管理员在操作。现在添加权限是一个搜索选择器,管理员需要给某个用户或管理员添加权限就必须搜索。权限在添加时为了多语言化必用标识,管理员在搜索时按照本地化语言搜索会方便很多。

orzlee还是把实现方法分享出来吧:

  1. GuardFilterHandelTrait主要功能是按guard_name过滤掉不属于附加用户的权限,防止多guard_name权限混乱。

     use Laravel\Nova\Http\Requests\NovaRequest;
    
     trait GuardFilterHandelTrait {
    
         /**
          * Override the applyFilters method to add the guard_name condition when filtering
          *
          * @param  \Laravel\Nova\Http\Requests\NovaRequest $request
          * @param  \Illuminate\Database\Eloquent\Builder $query
          * @param  array $filters
          *
          * @return \Illuminate\Database\Eloquent\Builder
          */
         protected static function applyFilters(NovaRequest $request, $query, array $filters) {
             $query = parent::applyFilters($request, $query, $filters);
             if ($model = head($request->__memoized)) {
                 $guard_name = $model->guard_name ?? self::getGuardForModel(get_class($model));
                 $query->where('guard_name', $guard_name);
             }
    
             return $query;
         }
    
         /**
          * @param string model
          *
          * @return string|null
          */
         public static function getGuardForModel(string $model) {
    
             return collect(config('auth.guards'))
                 ->map(function ($guard) {
                     return config("auth.providers.{$guard['provider']}.model");
                 })->search($model);
         }
     }
    
  2. PermissionSearchTranslationTrait搜索权限时会按照本地化语言反向找出对应权限。

     trait PermissionSearchTranslationTrait {
    
         use GuardFilterHandelTrait;
    
         /**
          * Override the applyFilters method,title field translation
          */
         public function title() {
    
             return __('laravel-nova-permission::permissions.display_names.'.$this->name);
         }
    
         /**
          * Rewrite the applySearch method to apply translation field search
          *
          * @param  \Illuminate\Database\Eloquent\Builder  $query
          * @param  string  $search
          * @return \Illuminate\Database\Eloquent\Builder
          */
         protected static function applySearch($query, $search)
         {
             return $query->where(function ($query) use ($search) {
                 if (is_numeric($search) && in_array($query->getModel()->getKeyType(), ['int', 'integer'])) {
                     $query->orWhere($query->getModel()->getQualifiedKeyName(), $search);
                 }
    
                 $model = $query->getModel();
    
                 $connectionType = $query->getModel()->getConnection()->getDriverName();
    
                 $likeOperator = $connectionType == 'pgsql' ? 'ilike' : 'like';
    
                 $trans_search = array_keys(preg_grep("/$search/",array_dot(__('laravel-nova-permission::permissions.display_names'))));
    
                 foreach (static::searchableColumns() as $column) {
                     $qualify_column = $model->qualifyColumn($column);
                     foreach ($trans_search as $t_search){
                         $query->orWhere($qualify_column, $likeOperator, '%'.$t_search.'%');
                     }
                     $query->orWhere($qualify_column, $likeOperator, '%'.$search.'%');
                 }
             });
         }
     }
  3. 自定义Role & Permission资源(注意命名空间替换成自己项目对应的):

     use App\Traits\Resources\GuardFilterHandelTrait;
     use \Insenseanalytics\LaravelNovaPermission\Role as BaseRole;
    
     class Role extends BaseRole {
         use GuardFilterHandelTrait;
     }
     use App\Traits\Resources\PermissionSearchTranslationTrait;
     use \Insenseanalytics\LaravelNovaPermission\Permission as BasePermission;
    
     class Permission extends BasePermission {
         use PermissionSearchTranslationTrait;
     }
  4. 按照作者文档自定义角色和权限类:

     // in app/Providers/NovaServiceProvider.php
    
     public function tools()
     {
         return [
             // ...
             \Insenseanalytics\LaravelNovaPermission\LaravelNovaPermission::make()
                 ->roleResource(CustomRole::class)
                 ->permissionResource(CustomPermission::class),
         ];
     }
  5. 如果你还没有发布语言文件请执行:

     php artisan vendor:publish --provider="Insenseanalytics\LaravelNovaPermission\NovaPermissionServiceProvider"

    创建自己本地化语言文件,例如在项目路径./resources/lang/vendor/laravel-nova-permission/创建一个zh-CN文件夹,复制en中的所有文件到zh-CN,然后自己翻译。display_names就是权限name对应的本地化翻译。

         'display_names' => [
         'test'   => '测试',
         'delete' => '删除',
         'edit'   => '编辑',
         'create' => '创建',
     ],

现在应该支持本地化权限搜索了。或者可以直接使用包jianminlee/laravel-nova-filter

该包还支持基于laravel nova的权限验证,非常方便,在nova资源中使用PermissionsBasedAuthTrait Trait,然后定义一个静态变量:

class YourNovaResource extends Resource
{
    use \Insenseanalytics\LaravelNovaPermission\PermissionsBasedAuthTrait;
    public static $permissionsForAbilities = [
      'viewAny' => 'view products',
      'view' => 'view products',
      'create' => 'create products',
      'update' => 'update products',
      'delete' => 'delete products',
      'restore' => 'restore products',
      'forceDelete' => 'forceDelete products',
      'addAttribute' => 'add product attributes',
      'attachAttribute' => 'attach product attributes',
      'detachAttribute' => 'detach product attributes',
    ];
    ....
}

详细文档可以取项目地址查看。insenseanalytics/laravel-nova-permission

3. DigitalCloud/nova-permission-tool:

今天刚刚发布的的,该包根据资源自动生成对应权限,听上去高大上,但是会多出不少多余的权限。因为我们的资源不一定会实现所有功能,到时候添加权限就会眼花缭乱了。orzlee认为还是自定义的好。

作者还自定义角色和权限的字段,在操作上体验好很多。凡是都有两面性,如果项目比较大(权限多)的情况下体验就可想而知了。

结语

这三款包都各有特色,同一功能的包就有这么多,这些还都只是基于spatie laravel-permission的,还有其他作者自己实现的权限管理工具。回过头来,还是得靠自己,工具再多也不一定都适合自己的项目,好好学Vue吧!

本原创文章未经允许不得转载 | 当前页面:OrzLee » laravel nova 权限管理工具

评论