علیرضا حسینی زاده
یه برنامه نویس
مدیر فنی افرافایل
درباره من
ساختن لینک امن و زمان دار برای دانلود فایل و ویدئو در لاراول ارسال شده در ۰۷ بهمن ۱۴۰۰
سلام یکی از دوستان سوال کرده بود چطور روی پروژه لاراولی لینک امن و زمان دار بسازیم تا دسترسی به فایل هامون لینک مستقیم نباشه و بتونیم امنیت فایل هامون رو تامین کنیم. این آموزش رو تصمیم گرفتم کامل کنم تا بقیه هم استفاده کنن
برای شروع باید بگم که لاراول چندین درایور برای مدیریت فایل ها داره مثل s3 local ftp local و… که شما میتونید توی کانفیگ filesystems اون رو مشخص کنید. به نظر من اگر فایل هاتون حجم زیادی دارن و زیاده استفاده از یک سرور دیگه برای ذخیره سازی و مدیریت فایل هاتون بهترین گزینست که روی این سرور s3 نصب کنید و با درایور s3 لاراول باهاش ارتباط برقرار کنید. ولی اگه حجم فایل هاتون زیاد نیست همون local storage جوابگو هست ولی در نهایت بدترین انتخاب پوشه public هست که دیدم زیاد استفاده میشه
توی این آموزش استفاده از storage رو برای ساخت لینک امن توضیح میدم اگر public استفاده کنید که کلا نمیشه لینک امن ساخت و اگه s3 استفاده کنید خود این سرور امکان ساخت لینک امن و زمان دار رو داخلش داره و نیازی نیست کار زیادی انجام بدید فقط درخواست یک لینک مستقیم زمان دار بهش بدید بقیه کارها رو خودش میکنه
تغییر درایور فایل سیستم لاراول به storage و آپلود و دانلود فایل از اون
به صورت دیفالت لاراول از این درایور استفاده میکنه پس نیازی نیست کانفیگ چیزی رو تغییر بدید فقط اگر دو بخش برای آپلود میخواین داشته باشید یکی با دسترسی مستقیم و پابلیک و یه بخش برای فایل های خصوصی که نباید مستقیم دانلود بشن کانفیگ زیر رو به مقدار disks در کانفیگ filesystems اضافه کنید:
'private' => [
'driver' => 'local',
'root' => storage_path('app/private'),
],
این کانفیگ یک دیسک جدید برای ما ایجاد میکنه که بتونیم فایل های خصوصی خودمون رو توی پوشه app/private ذخیره کنیم و هرزمان خواستیم دریافت کنیم که این آدرس دسترسی مستقیم نداره مگر از طریق فایل سیستم لاراول
خب چون احتمالا همه بلد باشید بخش آپلود و دریافت از این بخش رو زیاد توضیح نمیدم فقط یک مثال براتون میزنم
آپلود فایل در دیسک خصوصیمون
$name = Storage::disk('private')->put('files', $request->file);
دریافت از این دیسک
return Storage::disk('private')->download($path);
خب میریم برای مرحله بعدی
ساخت لینک امن و زمان دار مستقیم برای دانلود فایل ها در لاراول
برای ساخت لینک امن باید مقدار آي پی کاربر و یک زمان محدود مشخص کنیم تا لینک مستقیم فقط برای همون کاربر و مثلا تا ۵ دقیقه دیگه فقط کار کنه و این لینک هم مستقیم باشه که طرف بتونه تو نرم افزارهای دانلودکننده ازش استفاده کنه
برای شروع یک route جدید به شکل زیر ایجاد میکنیم:
Route::get('download/{file}', 'DownloadController@Download')->name('download.file');
این route که سادست و چیز خاصی برای توضیح نداره که بهتون بگم
میریم توی .env و یک کلید عجیب قریب ادد میکنیم که بتونیم با این مقدار لینک هامون رو رمزنگاری کنیم تا کسی نتونه بازشون کنه و فقط با این کلید بتونیم اونا رو باز کنیم، مثل زیر مقدارش رو هم میتونید مثل من با مشت بکوبید روی کیبورد و هرچی تایپ شد رو استفاده کنید
FILE_HASH_DOWNLOAD=kjasd81dsa932hj4kjsadiad932sad831DKF
خب حالا میریم جایی که میخوایم لینک امن بسازیم من پیشنهاد میکنم توی مدل یه فانکشن ادد کنید به اسم مثلا download که این لینک امن رو برمیگردونه و هرجایی از اپلیکیشن خواستید اینو صدا بزنید و یک لینک امن بگیرید برای مثال:
public function download()
{
if ($this->name){
$timestamp = now()->addHours(12)->timestamp;
$hash = Hash::make(env('FILE_HASH_DOWNLOAD') . $this->name . $timestamp . request()->ip());
return route('download.file',['file' => $this->id, 'mac' => $hash, 't' => $timestamp]);
} else {
return "#";
}
}
خب یه توضیح بدم اینجا
اینجا مثلا مدل File من هست برای مثال من فایل هامو که ذخیره میکنم توی دیتابیس files اونا رو ذخیره میکنم و اسم name هم اسم فایل من میشه حالا شما هر ساختار دیگه ای دارید مشکلی نیست از همین ساختار استفاده کنید مثلا توی دیتابیس article فایلتون رو تو فیلد file ذخیره میکنید برای این حالت این فانکشن download رو توی مدل article بزارید و خط اول به جای name از file استفاده کنید.
مقدار timestamp ما با کربن مشخص میکنیم که این لینک ما تا چقدر بعد اعتبار داشته باشه که من زدم ۱۲ ساعت ولی شما میتونید هر مقداری از ثانیه تا چند سال رو بهش بدید
مقدار hash که خیلی مهمه یک کلید اعتبارسنجی داریم درست میکنیم که توی url نهایی به کاربر نشونش میدیم و بر اساس اون چک میکنیم که کاربر درسته یا نه خب توی این کلید ما چند مقدار رو میزاریم.
اول اون مقدار کلید اصلی سرور رو از env گذاشتیم،
بعد یک مقدار دلخواه از مشخصه این فایل (که کاربر با ساخت یک کلید نتونه همه فایل ها رو دانلود کنه و فقط اجازه دانلود همین فایل رو داشته باشه)
بعد مقدار timestamp رو میزاریم که کسی نتونه تغییرش بده
و بعد مقدار ip کاربر رو میزاریم اگه میخواین مقدار ip کاربر چک نشه این مقدار رو بردارید ضروری نیست
و تمام در نهایت route رو return میکنیم به همراه مقدار های file که باهاش بتونیم بفهمیم چه فایلی میخواد دانلود بشه، مقدار کلید که اسمش رو گذاشتیم mac و مقدار t که زمان رو برمیگردونه برامون
حالا هرجایی خواستید با صدا زدن download میتونید لینک امن رو تحویل بگیرید مثل زیر:
$file = File::find(1);
$link = $file->download();
یا
$article = Article::find(1);
$link = $article->download();
حالا میریم و فاکنشن کنترلر DownloadController رو به شکل زیر ایجاد میکنیم که اگر کسی روی لینک امن ما کلیک کرد میاد اینجا:
public function Download(File $file)
{
$hash = env('FILE_HASH_DOWNLOAD') . $file->name . request('t') . request()->ip();
if(request('t') > now()->timestamp && Hash::check($hash , request('mac'))) {
$path = 'files/'.$file->name;
return Storage::disk('private')->download($path);
} else {
return "لینک منقضی شده است، لطفا مجددا بر روی دکمه دانلود کلیک کنید.";
}
}
خب اینجا هم دوباره کلید رو میسازیم دقیقا مثل چیزی که تو زمان ساخت لینک ساختیم
بعد زمان رو بررسی میکنیم که نگذشته باشه و بعدش مقدار کلیدی که خودمون ساختیم و مقایسه میکنیم با مقدار کلید کاربر که تو url برای ما فرستاده اگر یکی بود اجازه دانلود داده میشه و میتونید دانلود کردن فایل رو برگردونید سمت کاربر
در غیر اینصورت کاربر یا داره تقلب میکنه یا زمان لینک امنش تموم شده و میتونید آلرت برگردونید.
به همین راحتی
فقط دقت کنید اگر مقدار ip رو توی کلید زمان ساخت لینک حذف کردید اینطرف هم باید حذف کنید.
الان لینک ما هم مستقیمه هم برای ip کاربر محدود شده و هم تا یه مدت زمان مشخص کار میکنه به همین راحتی
امیدوارم آموزش خوبی بوده باشه
همگی موفق باشید
عالی بود . ممنون از شما
خواهش میکنم
عالی بود واقعا،فقط یه سوال دارم الان من دوتا درایو دارم که یکیش public مثلا یکیشم private
سوالم اینه چطور فایلی که تو public ذخیره میشه رو موقع فراخوانی فایل به این شکل لینک بدیم:
article/image/test.png با این توضیح که در واقع ت. storage/public ذخیره شده
سوال دیگم برای فایل های پرایوت که اومدم مثلا این کاری که تو مقاله گفتیدو انجام دادم چه جوری مثلا آنلاین پخشش کنم مثل پادکست یا دوره آموزشی
خواهش میکنم
برای سوال اولت این بخش مستندات رو بخون https://laravel.com/docs/9.x/filesystem#the-public-disk که میاد یک سیملینک از استوریج به پوشه پابلیکت میزنه و بعدش میتونی تو همون مدلی که میخوای لینک مستقیم بدی و فراخوانی کنی فایل ها رو
برای سوال دومت برای پخش آنلاین هر فرمتی فرق نمیکنه همون لینک دانلودی که میسازه رو باید تو تگ html خودت بزاری و پخش میشه میخواد video باشه یا audio باشه