返回

Laravel File Manager 用户访问限制及解决方案

php

用户访问权限限制:Laravel File Manager 文件管理

在使用 alexusmai/laravel-file-manager 扩展包时,我们常常需要限制用户只能访问其自身的目录。这个需求常见于需要为每个用户提供独立的存储空间的场景,例如网盘应用。 本文分析一种用户访问限制失败的情形, 并给出解决方案。

问题分析

在典型配置中,我们会在用户注册时为每个用户创建一个独特的目录,目录名通常存储于数据库用户表中。 同时, 我们希望通过 Laravel 的配置系统,以及中间件在运行时动态更改 file-manager 使用的 filesystem 驱动设置。 这样做可以隔离不同用户的存储空间,理论上能确保用户只能访问自己的文件。 然而,实际使用中可能会遇到以下情况:即使 filesystem 驱动配置动态更新了,用户依然能够访问根目录的全部内容,这表示动态设置没有按预期生效。

问题主要根源在于, alexusmai/laravel-file-manager 在读取配置文件的时候并不会读取实时更新过的文件系统设置,而是只在首次加载 file-manager 配置的时候加载一次。虽然中间件更新了配置,但是 file-manager 并不知道设置已被更改,它还是用的旧的配置信息。这导致文件管理器实际访问的路径还是最开始在配置文件里设置的 root,而不是中间件修改后的路径。

解决方案

这里有几种方案解决这个问题,以满足用户的动态目录访问限制需求。

方案一:覆写文件系统服务

最直接的解决方法是自定义一个文件系统服务,它能够动态地决定每个用户的文件根路径。通过这种方式, file-manager 每次获取文件列表时都能正确使用最新的用户路径配置。

  1. 创建一个服务提供者 :

    可以使用 artisan 命令创建一个新的服务提供者, 例如:php artisan make:provider FileManagerServiceProvider.

  2. 注册自定义驱动 :

    在新创建的 FileManagerServiceProvider 中的 register 方法里, 覆盖 Laravel 的 filesystem 驱动。

    <?php
    
    namespace App\Providers;
    
    use Illuminate\Support\Facades\Auth;
    use Illuminate\Support\ServiceProvider;
    use Illuminate\Filesystem\FilesystemManager;
    use Illuminate\Support\Facades\Config;
    
    class FileManagerServiceProvider extends ServiceProvider
    {
        /**
         * Register services.
         */
        public function register(): void
        {
           $this->app->extend('filesystem', function (FilesystemManager $filesystem) {
            $filesystem->extend('user-local', function ($app, $config) {
    
              if (Auth::guard("user")->check()) {
                    $userDirectory = storage_path("app/private/"). Auth::guard("user")->user()->base_directory_name;
    
                 return new \League\Flysystem\Filesystem(new \League\Flysystem\Local\LocalFilesystemAdapter($userDirectory));
    
    
                }
    
              throw new \Exception("User not authenticated or can't get user folder.");
    
    
              });
                return $filesystem;
          });
    
          $this->app->booted(function () {
              Config::set('file-manager.diskList', ['user-local']); //替换 file-manager 使用的磁盘为自定义驱动
          });
    
        }
    
        /**
         * Bootstrap services.
         */
        public function boot(): void
        {
    
        }
    }
    
  3. 配置config/filesystems.php :

config/filesystems.php 配置文件中新增user-localdisk 配置 ( 注意: 只需要 user-local 这个 disk , 其他配置均可以在注册的服务提供者中配置):

 'disks' => [
         ...

         'user-local' => [
                 'driver' => 'user-local',
          ],

          ...
]
 ```

4.  **注册服务提供者** :
 确保服务提供者已注册,通常位于`config/app.php` 配置文件中的 `providers` 数组中。
 ```php
    'providers' => [
       ...
       App\Providers\FileManagerServiceProvider::class,
      ],

此方案的优势是配置简洁,动态调整方便,并且实现了更精细的文件系统控制,同时能兼容 file-manager 配置,无需额外的逻辑进行路径切换。

方案二: 重写控制器方法(不推荐)

另一种可选但不推荐的方法是,直接修改 file-manager 扩展包的控制器方法。这种方式相对复杂且容易在更新包的时候出现问题,不建议使用。它涉及到深入到包的代码,找出获取路径的地方,然后通过中间件设置好的配置,进行修改,但是这样风险高。 这里不做详细。

安全建议

  • 数据验证和过滤 : 始终对用户输入的数据进行验证和过滤,以防止潜在的安全风险,比如路径遍历漏洞。

  • 权限控制 : 除动态目录访问限制外, 应该添加额外层级的权限控制,确保用户只能对自身拥有的文件进行操作。

  • 定期更新 : 定期更新使用的软件包版本,以修补任何已知安全漏洞。

通过上述方法,你可以实现每个用户拥有独立文件目录,并且确保了其目录访问权限的隔离。选择哪一种方案取决于项目复杂度和个人偏好,自定义服务驱动更加灵活和便于管理,可以作为一种比较好的方案。务必结合安全实践,对用户的数据和应用安全加以保障。