Laravel中如何对⼤⽂件进⾏加密
在项⽬中,⽤户可以上传任何⼤⼩的⽂件,出于安全⽬的,需要对这些⽂件进⾏静态加密。
Laravel 提供 encryption, 但它们主要⽤于加密值。使⽤ encrypt 助⼿⽅法对像图像这样的⼩⽂件进⾏加密可以很好地⼯作,但是在此过程中,⽂件内容需要加载到内存中,这对于⼤⽂件来说是个问题。
我已经搜索过⽤于解决此问题的软件包或解决⽅案,并遇到了这个 Stack Overflow 回答和这个 PHP 解决⽅案,该解决⽅案基本上是 Stack Overflow 所描述的解决⽅案的 PHP 实现。
我决定创建⼀个为 Laravel 设计的扩展包,使⽤简单,优雅的语法提供简单的⽂件加密 / 解密功能。
我将其称为 FileVault 包,您可以 GitHub 上查看它。如果您想跳过本教程,可以直接转到 GitHub 存储库并开始使⽤此软件包。该扩展包包括了详细的使⽤⽂档。
教程
在这个教程中,我会详细描述加密⼤⽂件需要的所有步骤。
⾸先,使⽤ Laravel 安装器,命名为security-app:
laravel new security-app
在撰写本教程时,我正在使⽤ Laravel v6.5.2。
因为我们已经使⽤了 Laravel 安装程序,所以我们已经⽣成了⼀个应⽤程序密钥并将其添加到我们的 .env ⽂件中。如果您使⽤其他安装⽅法,请不要忘记使⽤以下⽅法⽣成新的应⽤程序密钥:
php artisan key:generate
因为我们正在使⽤ Laravel Valet,所以应该已经为我们创建了 st 域名。如果使⽤其他开发环境,则应添加⼀个本地域名指向新项⽬。
由于⾃ Laravel 6 以来前端脚⼿架已被移⾄ Laravel UI 中,因此我们将安装 laravel/ui 扩展包。
composer require laravel/ui — dev
接下来,我们将安ootstrap和auth脚⼿架:
php artisan ui bootstrap --auth
并编译所有内容:
npm install && npm run dev
我们还需要在 .env ⽂件中配置数据库访问凭据并运⾏初始迁移:
php artisan migrate
现在,我们可以创建⼀个新⽤户并登录查看⽤户仪表板。
注意:在本演⽰中,我们将创建⼀个基本的上传表单,但是在您的应⽤程序中,您应该考虑使⽤更复杂的上传功能,对⼤⽂件使⽤分块上传。
您可以使⽤⼀个⾮常好的扩展包是 pion/laravel-chunk-upload.
Laravel Auth 脚⼿架为我们创建了⼀个 /home 路由,⼀个 HomeController 和⼀个 home.blade.php 视图⽂件。
让我们编辑 home.blade.php ⽂件并添加⼀个表单和⼀个上传字段:
<form action="{{ route('uploadFile') }}" method="post" enctype="multipart/form-data" class="my-4">
@csrf
<div class="form-group">
<div class="custom-file">
<input type="file" class="custom-file-input" id="userFile" name="userFile">
<label class="custom-file-label" for="userFile">Choose a file</label>
</div>
</div>
<button type="submit" class="btn btn-primary">Upload</button>
@if (session()->has('message'))
<div class="alert alert-success mt-3">
{{ session('message') }}
</div>
@endif
</form>
然后添加相应的路由:
Route::post(‘/home’, ‘HomeController@store’)->name(‘uploadFile’);
在 HomeController 中新增 store ⽅法。此⽅法会将上传的⽂件存储在具有当前⽤户 ID 的⽂件⽬录中 (storage/app/files/{user-id}) 。
注意:这是不正确的做法,不应在⽣产环境中使⽤。为了使本教程更加⼩巧,我们使⽤⽂件系统来获取⽤户的⽂件,但是在⽣产环境中,需要使⽤数据库来跟踪每个⽤户上传的⽂件。
<?php
/**
* Store a user uploaded file
*
* @param  \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
if ($request->hasFile('userFile') && $request->file('userFile')->isValid()) {
Storage::putFile('files/' . auth()->user()->id, $request->file('userFile'));
}
return redirect()->route('home')->with('message', 'Upload complete');
}
到了加密⽤户上传⽂件的阶段。我们将安装扩展包:
composer require soarecostin/file-vault
该软件包允许访问 FileVault 门⾯,其中提供了⼀些⽤于加密和解密⽂件的⽅法,还提供了⼀些⽅法来设置选项,例如为每个⽂件设置不同的加密密钥,或指定该⽂件的 Laravel ⽂件系统磁盘。
我们将使⽤ FileVault::encrypt($file) ⽅法来加密⽤户上传的⽂件。此功能将删除原始的未加密⽂件,并将其替换为具有相同名称和附加 .enc 扩展名的⽂件。
如果您想使⽤不同的名称命名⽂件,则可以将所需的名称作为第⼆个参数传递给 encrypt ⽅法。如果您想保留原始⽂件,可以使⽤encryptCopy ⽅法。
这就是我们的 store ⽅法现在的样⼦:
<?php
/**
* Store a user uploaded file
*
* @param  \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
if ($request->hasFile('userFile') && $request->file('userFile')->isValid()) {
$filename = Storage::putFile('files/' . auth()->user()->id, $request->file('userFile'));
// Check to see if we have a valid file uploaded
if ($filename) {
FileVault::encrypt($filename);
}
大文件发送}
return redirect()->route('home')->with('message', 'Upload complete');
}
接下来,我们需要查看所有⽤户上传的⽂件,还需要⼀种下载它们的⽅法。
我们将在 HomeController 中创建⼀个新的 downloadFile 路由和⼀个新的 downloadFile ⽅法:
Route::get(‘/files/{filename}’, ‘HomeController@downloadFile’)->name(‘downloadFile’);
<?php
/**
* Download a file
*
* @param  string  $filename
* @return \Illuminate\Http\Response
*/
public function downloadFile($filename)
{
// Basic validation to check if the file exists and is in the user directory
if (!Storage::has('files/' . auth()->user()->id . '/' . $filename)) {
abort(404);
}
return response()->streamDownload(function () use ($filename) {
FileVault::streamDecrypt('files/' . auth()->user()->id . '/' . $filename);
}, Str::replaceLast('.enc', '', $filename));
}
downloadFile 使⽤ Laravel 原⽣的 streamDownload response, 接收⼀个回调.
在回调中,我们正在调⽤扩展包 FileVault 提供的 streamDecrypt ⽅法,它将对⽂件进⾏解密并将其逐段提供给streamDownload⽅法,从⽽允许您的⽤户直接下载解密⽂件。
现在,我们需要在上传表单下⽅显⽰所有⽤户的⽂件。为此,我们将 $files 变量从 HomeController 的 index ⽅法发送到 home.blade.php
视图⽂件,并在上传表格的下⾯显⽰⽤户⽂件。
<?php
/**
* Show the application dashboard.
*
* @return \Illuminate\Contracts\Support\Renderable
*/
public function index()
{
$files = Storage::files('files/' . auth()->user()->id);
return view('home', compact('files'));
}
home.blade.php
<ul class="list-group">
@forelse ($files as $file)
<li class="list-group-item">
<a href="{{ route('downloadFile', basename($file)) }}">
{{ basename($file) }}
</a>
</li>
@empty
<li class="list-group-item">You have no files</li>
@endforelse
</ul>
就是这样!我们现在在使⽤静态加密!我们创建了供⽤户上传⽂件的表单,对这些⽂件进⾏加密,并且仅在上传⽂件的⽤户要求时才对其解密。
当然,在⽣产中,需要采取更多的安全措施,⽽ FileVault 扩展包旨在这⽅⾯为您提供帮助。
如,您可能希望将⽤户上传的⼤⽂件存储在 Amazon S3 中,该扩展包⽀持⽂件加密 / 流解密。
您可能还想为每个⽤户或每个⽂件使⽤不同的加密密钥,这对于 FileVault 扩展包也是可能的。
资源
您可以在中到在本教程中创建的整个 Laravel 应⽤.