如何在Laravel 5中保护图片不被公开查看?

33

我已经安装了Laravel 5.0,并且已经进行了身份验证。一切都工作得很好。

我的网站只对已验证的成员开放。里面的内容仅限已验证的成员访问,但是网站中的图片并没有受到公共查看的保护。

任何人直接写入图片URL都可以看到该图片,即使该人没有登录系统。

http://www.somedomainname.net/images/users/userImage.jpg

我的问题:是否可能保护图片(上述URL示例)不被公开查看,换句话说,如果将图像的URL发送给任何人,那个人必须是会员并登录才能查看该图像。

这是否可能?如何实现?


你尝试过设置路由来保护图片路径吗? - haakym
不,但我会尝试下面的答案并让您知道。 - user4980957
@haakym,下面的例子对我有用。你说得没错,是在路由中。 - user4980957
5个回答

39

在 Laravel 5.x 文件夹中,可以保护图片使其不被公开查看

  • 在 Laravel 中创建 storage/app/images 文件夹,并将需要保护的图片从公共文件夹移动到该文件夹中。我选择 storage 文件夹是因为它已经有了写入权限,在上传图片时可以直接使用。

  • 你也可以选择其他位置创建 images 文件夹,但不能在公共文件夹内部创建,例如控制器文件夹内部还是在 Laravel 文件夹结构中合理的位置。接下来需要创建路由和图片控制器。

创建路由

Route::get('images/users/{user_id}/{slug}', [
     'as'         => 'images.show',
     'uses'       => 'ImagesController@show',
     'middleware' => 'auth',
]);

如果用户未登录,该路由将会将所有图片请求转发到认证页面。

创建ImagesController。

class ImagesController extends Controller {

    public function show($user_id, $slug)
    {
        $storagePath = storage_path('app/images/users/' . $user_id . '/' . $slug);
        return Image::make($storagePath)->response();
    }
}


编辑(注)

对于使用Laravel 5.2及更高版本的用户,Laravel引入了一种新且更好的文件服务方式,具有更小的开销(这种方式不会像答案中提到的那样重新生成文件):

文件响应

file方法可用于直接在用户的浏览器中显示文件(如图像或PDF),而不是启动下载。此方法将文件路径作为其第一个参数,并将头文件数组作为其第二个参数:

return response()->file($pathToFile);

return response()->file($pathToFile, $headers);
您可以根据自己的需求修改存储路径和文件/文件夹结构,这只是为了展示我是如何做到的以及它是如何工作的。
您还可以在控制器中添加条件,仅针对特定成员显示图像。
此外,还可以使用文件名、时间戳和其他变量对文件名进行哈希处理。
补充说明:有人问是否可以将此方法用作公共文件夹上传的替代方法,是的,但不建议这样做,因为在此解答中已经解释过。因此,即使您不打算保护它们,也可以使用相同的方法将图像上传到存储路径,只需按照相同的流程操作,但删除'middleware' => 'auth',即可。这样,您就不会在公共文件夹中授予777权限,而仍然拥有安全的上传环境。上述相同的解答还介绍了如何在没有身份验证的情况下使用此方法,以防有人使用它或提供替代解决方案。

不建议在没有保护的情况下进行普通图像上传,因为性能会比让Apache\Nginx处理图像服务要差得多。 - Amir Bar
@AmirBar,您有什么替代方案吗? - Maytham Fahmi
对于不需要保护的普通图像上传,请上传到公共文件夹并让nginx处理。 - Amir Bar
@AmirBar 这只是为了保护您的文件,如果您需要的话,我同意普通上传将在您的公共文件夹中进行,但最后一条说明是因为有人在另一个问题的评论中询问它,所以我添加了这个注释。 - Maytham Fahmi
1
我假设上面的 Image::make 方法来自于 intervention/image 包? - D Malan
@ Dipen,你有试着阅读“编辑(注)”部分吗?除此之外,我在“另外”部分提到了更多内容,为此我添加了链接以及另一个回答选项来解决这个问题https://dev59.com/l1oV5IYBdhLWcg3wkvl1#36477345并且可以在这个答案中找到其他的替代方法。 - Maytham Fahmi

19

在以前的项目中,我通过以下方式保护了上传内容:

创建了存储磁盘

config/filesystems.php
'myDisk' => [
        'driver' => 'local',
        'root' => storage_path('app/uploads'),
        'url' => env('APP_URL') . '/storage',
        'visibility' => 'private',
    ],

这将上传文件到\storage\app\uploads\,这个路径不对公众开放。

在您的控制器中保存文件:

Storage::disk('myDisk')->put('/ANY FOLDER NAME/' . $file, $data);

为了让用户查看文件并保护上传内容免受未授权访问,首先要检查磁盘上的文件是否存在:

public function returnFile($file)
{
    //This method will look for the file and get it from drive
    $path = storage_path('app/uploads/ANY FOLDER NAME/' . $file);
    try {
        $file = File::get($path);
        $type = File::mimeType($path);
        $response = Response::make($file, 200);
        $response->header("Content-Type", $type);
        return $response;
    } catch (FileNotFoundException $exception) {
        abort(404);
    }
}

如果用户有正确的访问权限,就提供文件服务:

 public function licenceFileShow($file)
{
    /**
     *Make sure the @param $file has a dot
     * Then check if the user has Admin Role. If true serve else
     */
    if (strpos($file, '.') !== false) {
        if (Auth::user()->hasAnyRole(['Admin'])) {
            /** Serve the file for the Admin*/
            return $this->returnFile($file);
        } else {
            /**Logic to check if the request is from file owner**/
            return $this->returnFile($file);
        }
    } else {
//Invalid file name given
        return redirect()->route('home');
    }
}

最后在 Web.php 路由文件中:

Route::get('uploads/user-files/{filename}', 'MiscController@licenceFileShow');

3
我实际上没有尝试过这个,但我发现Nginx auth_request 模块可以让您检查来自Laravel的身份验证,但仍然使用Nginx发送文件。它向给定的URL发送内部请求,并检查成功(2xx)或失败(4xx)的HTTP代码,在成功时让用户下载文件。
编辑:另一个选择是我尝试过并且似乎工作正常的东西。您可以使用X-Accel-Redirect -header从Nginx中提供文件。请求通过PHP进行,但是它不会将整个文件发送出去,而只是将文件位置发送到Nginx,然后由Nginx将其提供给客户端。

0

公共文件夹内的每个文件都可以在浏览器中访问。如果他们发现了文件名和存储路径,任何人都可以轻易获得该文件。

因此,更好的选择是将文件存储在公共文件夹之外,例如:/storage/app/private

现在执行以下步骤:

  1. 创建一个路由(例如:private/{file_name})

    Route::get('/private/{file_name}', [App\Http\Controllers\FileController::class, 'view'])->middleware(['auth'])->name('view.file');

  2. 在控制器中创建一个返回文件路径的函数。要创建控制器,请运行命令php artisan make:controller FileController

并将视图函数粘贴到FileController中

public function view($file)
    {
        $filePath = "notes/{$file}";
        
        if(Storage::exists($filePath)){
            return Storage::response($filePath);
        }
        abort(404);
    }

然后,在FileController中粘贴use Illuminate\Support\Facades\Storage;以进行存储

  1. 不要忘记根据您的要求(例如:auth)分配中间件(在路由或控制器中)

现在,只有那些具有访问该中间件的权限的人才能通过名为view.file的路由访问该文件


0

如果我理解你的意思是这样的!

 Route::post('/download/{id}', function(Request $request , $id){
  {

     if(\Auth::user()->id == $id) {
         return \Storage::download($request->f);
     } 
     else {
         \Session::flash('error' , 'Access  deny');
         return back();
     }
  }

 })->name('download')->middleware('auth:owner,admin,web');

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接