Створення одноразових URL-адрес

119

Від автора: при розробці веб-додатків іноді виникає завдання створення одноразових посилань для користувачів. Тобто посилань, які можна використовувати тільки один раз. Такі посилання можна використовувати як для скачування різного матеріалу на Вашому сайті, так і для активації нового зареєстрованого користувача. Тому в даному уроці ми з Вами розглянемо створення одноразових посилань на прикладі посилання для скачування файлу.

Створення одноразових URL-адресСтворення одноразових URL-адрес

1. Постановка задачі

Для початку давайте умовимося, що одноразову посилання ми будемо створювати для скачування файлу. Даний файл будемо зберігати в папці file і це звичайне зображення, яке необхідно для прикладу.

Далі, створення одноразової посилання полягає в генерації унікальною рядка, яку необхідно передати в спеціальний файл обробник. Даний файл виконає необхідні перевірки і якщо посилання вірна (унікальна рядок), значить, відкриється доступ для скачування файлу. Звичайно, для кожного користувача необхідно створювати окрему посилання, і тому всі створені посилання необхідно десь зберігати. Як сховища посилань можна вибрати базу даних, або звичайний текстовий файл. Ми в даному уроці будемо використовувати текстовий файл для зберігання створених посилань.

І останнє, необхідно забезпечити одноразове використання посилання. Тобто, будь-яку створену посилання, користувач зможе використовувати не більше одного разу. При повторному використанні посилання, доступ на скачування файлу необхідно заборонити. Тепер давайте приступимо до реалізації поставленої задачі.

2. Створення одноразових посилань

Отже, на локальному комп’ютері в папці lessons, я створив папку onetime, в якій ми будемо зберігати наш майбутній скрипт. Тобто шлях до папки з скриптом буде наступний: http://localhost/lessons/onetime/

Тепер, давайте створимо новий файл, за назвою get_hash.php, який буде генерувати унікальну рядок і тим самим формувати одноразову посилання для скачування файлу. Першим ділом, генеруємо унікальну рядок:

$hash = md5(microtime());

Для цього використовуємо функцію microtime(), яка повертає поточну мітку часу. Тобто в кожен момент часу ми будемо отримувати різне значення. Нагадаю, що обчислене значення це рядок виду: «msec sec», де sec являє собою поточний час, що минув з початку Епохи Unix (1 січня 1970 0:00:00 GMT) в секундах, а msec — це кількість мікросекунд, що минули після sec. Потім опрацюємо отримане значення функцією md5(), тобто зашифруем отриманий рядок однонаправленим алгоритмом шифрування md5 (односпрямоване – означає, що можна зашифрувати дані, але не можна розшифрувати). Давайте виведемо на екран значення змінної $hash:

Створення одноразових URL-адрес

Як Ви бачите, рядок успішно генерується. Далі, створюємо змінну $file, в якій будемо зберігати ім’я файлу, в якому будуть зберігатися створені унікальні рядки для посилань:

Тепер, створену рядок необхідно зберегти у файлі, тому, давайте відкриємо текстовий файл для запису:

$fd = fopen($file,»a»);
if(!$fd) {
exit(«Не можливо відкрити файл»);
}

Для відкриття файлу для запису використовуємо функцію fopen(). Яка відкриває файл в певному режимі. Ми будемо працювати в режимі a, тобто, відкриваємо файл тільки для запису і переміщаємо курсор у кінець файлу (якщо ми викликаємо функцію в перший раз, то відкривається файл буде створений).

Тепер, для запобігання появи помилок, пов’язаних з постійним зверненням до файлу, необхідно блокувати його на час запису даних. Тобто при генерації посилання, виконується запис унікальною рядки в файл. Потім, ми з Вами будемо постійно зчитувати дані файлу, для перевірки правильності рядків. При цьому можуть виникати помилки при численних зверненнях до файлу в один і той же момент часу (тобто запис і зчитування даних відбувається практично одночасно). Тому перед записом даних ми з Вами заблокуємо файл функцією flock():

if(!flock($fd,LOCK_EX)) {
exit(«Блокування файлу не вдалася»);
}

Нагадаю, що дана функція накладає блокування на файл. Режим роботи якої визначається константами, що передаються другим параметром до даної функції. Режим LOCK_EX – накладає ексклюзивну блокування (запис). І при цьому всі можливі звернення файлу будуть заборонені, тобто поки він заблокований. Окрім запису даних у файл з нашого скрипта, що власне ми і виконаємо:

fwrite($fd,$hash.»\n»);

Для запису ми використовуємо функцію fwrite, і записуємо згенероване унікальну рядок. Тепер давайте розблокуємо файл, тобто, дозволимо звернення до нього з неба і закриємо файл:

if(!flock($fd,LOCK_UN)) {
exit(«Не можливо розблокувати файл»);
}
fclose($fd);

Для розблокування доступу до файлу, ми використовуємо функцію flock(), і передаємо константу LOCK_UN, тобто зняття блокувань. Тепер, давайте кілька разів оновимо скрипт і подивимося, що записано у файл:

Створення одноразових URL-адрес

Як Ви бачите, дані в файл успішно записані. Тепер необхідно сформувати посилання і відобразити її на екрані для користувача:

$path = substr($_SERVER[‘PHP_SELF’],0,strrpos($_SERVER[‘PHP_SELF’],»/»));
echo «

Ваша посилання для скачування

«;
echo «http://».$_SERVER[‘HTTP_HOST’].$path.»/get_file.php?hash=».$hash.»»;

Для цього нам необхідно визначити папку, в якій міститься скрипт. Тому, використовуючи функцію substr, ми отримуємо ім’я папки і зберігаємо в змінної $path. Потім виводимо на екран посилання, використовуючи клітинку суперглобального масиву $_SERVER[‘HTTP_HOST’] і отриману змінної $path. Так само додаємо ім’я файлу обробника, в нашому випадку це get_file.php і, використовуючи GET параметри, передаємо згенеровану рядок. Тепер давайте перейдемо в браузер і подивимося, що у нас вийшло:

Створення одноразових URL-адрес

Як Ви бачите, посилання виводиться, унікальна рядок генерується і записується в текстовий файл. Значить, ми все зробили правильно. Тепер на всяк випадок приведу повний код файлу, get_hash.php:

Ваша посилання для скачування

«;
echo «http://».$_SERVER[‘HTTP_HOST’].$path.»/get_file.php?hash=».$hash.»»;
?>

Тепер необхідно створити скрипт, який отримає передані дані і виконає необхідні перевірки.

3. Перевірка посилань

Отже, створюємо порожній файл get_file.php, в якому ми будемо перевіряти правильність посилання і якщо все вірно, надавати доступ для скачування файлу. Першим ділом, створюємо дві змінні, які будуть містити ім’я файлу для скачування (посилання на який ми створюємо), і ім’я файлу з унікальними рядками посилань:

$s_file = «file/Desert.jpg»;
$file = «get_url.txt»;

Потім, створюємо змінну, яка буде містити дозвіл на скачування файлу. Тобто, якщо значення цієї змінної дорівнює TRUE, значить, необхідно дозволити завантаження посилання:

$check = FALSE;

Даний файл передаються методом GET (через рядок), значить в суперглобальном масиві GET, буде створено осередок hash. Тому, збережемо значення комірки, так само в однієї змінної:

$hash = $_GET[‘hash’];

Тепер, так як ми знаємо, що унікальна рядок, отримана з допомогою шифрування md5, значить унікальна рядок має містити рівно 32 символу. Тому, давайте виконаємо першу перевірку:

if(strlen($hash) != 32) {
exit(«Не правильныая посилання»);
}

Тепер вважаємо файл з унікальними рядками і весь його вміст порядково, збережемо в комірках масиву:

$arr = file($file);

Тобто масив $arr, містить в кожній своїй комірці згенеровану рядок з файлу. Приміром, якщо роздрукувати його функцією print_r(), ми побачимо наступне:

Створення одноразових URL-адрес

Тепер, так як весь вміст файлу, міститься в одному масиві, ми можемо перевірити, чи є рядок, передана через GET параметри (значення змінної $hash) в одній з комірок масиву $arr. І якщо дійсно є збіг, значить, доступ до файлу можна відкрити.

Але, адже нам ще потрібно забезпечити одноразове використання кожного посилання. Тому, ми очистимо файл з унікальними рядками. Потім у циклі обійдемо масив $arr і на кожній ітерації циклу будемо порівнювати поточне значення комірки масиву, з значенням змінної $hash. Якщо збіг не знайдено, значення комірки запишемо назад у файл, якщо ж знайдено збіг, то записувати нічого не будемо, тільки в змінну $check збережемо значення TRUE. При цьому ми забезпечимо одноразове використання посилання. Так як якщо посилання пройде перевірку (буде знайдено збіг з кодом файлу), її унікальний код при перезаписі не потрапить в текстовий файл. І повторно використовувати дану посилання (унікальний код для перевірки) буде не можливо.

Тож, відкриваємо файл для запису в режимі w (відкриває файл тільки для запису і поміщає курсор у початок файлу і обрізає файл нульової довжини.):

$fd = fopen($file,»w»);
if(!$fd) {
exit(«Не можливо відкрити файл»);
}

Потім, блокуємо файл, від доступу, поки йде запис:

if(!flock($fd,LOCK_EX)) {
exit(«Блокування файлу не вдалася»);
}

Формуємо цикл, який перевірить і перезапише унікальні коди в файл, де не знайдено збігів з значенням змінної $hash:

for ($i = 0; count($arr) > $i; $i++) {
if($hash == rtrim($arr[$i])) {
$check = TRUE;
}
else {
fwrite($fd,$arr[$i]);
}
}

Далі, знімаємо блокування і закриваємо файл:

if(!flock($fd,LOCK_UN)) {
exit(«Не можливо розблокувати файл»);
}
fclose($fd);

Тепер залишилося тільки перевірити, що міститься в змінній $check, і якщо в ній міститься TRUE, значить можна віддати файл на завантаження. Наприклад, можна поступити наступним чином:

if($check) {
header(«Content-Опис: File Transfer»);
header(«Content-Type: image/jpeg»);
header(«Content-Disposition: attachment; filename=».basename($s_file));
header(«Content-Transfer-Encoding:binary»);
header(«Content-Length: «.filesize($s_file));
ob_clean();
flush();
readfile($s_file);
exit();
}
else {
exit(«Не правильна посилання!!!»);
}

Тобто, перевіряємо значення змінної $check, і якщо так, то ми відправимо кілька заголовків в браузер. Тим самим повідомляючи йому, що тип контенту – це зображення формату jpeg (header(«Content-Type: image/jpeg»);). Так само, що зображення не потрібно відобразити на екрані, а передати для скачування, тобто, що б браузер відкрив вікно скачування файлу (header(«Content-Disposition: attachment; filename=».basename($s_file));). Ну і звичайно ж передаємо ім’я файлу і його розмір (filename=».basename($s_file)); header(«Content-Length: «.filesize($s_file));).

Далі очищаємо буфер обміну і читаємо файл за допомогою функції readfile(). І не забуваємо вийти з скрипта після прочитання файлу. Якщо ж у змінній $check міститься інше значення TRUE, значить перевірка не вдалася і необхідно заборонити доступ до скачиваемому файлу. Тепер на всяк випадок приведу повний код файлу get_file.php:

$i; $i++) {
if($hash == rtrim($arr[$i])) {
$check = TRUE;
}
else {
fwrite($fd,$arr[$i]);
}
}
if(!flock($fd,LOCK_UN)) {
exit(«Не можливо розблокувати файл»);
}
fclose($fd);
if($check) {
header(«Content-Опис: File Transfer»);
header(«Content-Type: image/jpeg»);
header(«Content-Disposition: attachment; filename=».basename($s_file));
header(«Content-Transfer-Encoding:binary»);
header(«Content-Length: «.filesize($s_file));
ob_clean();
flush();
readfile($s_file);
exit();
}
else {
exit(«Не правильна посилання!!!»);
}
?>

Ось ми з Вами і реалізували механізм створення одноразових посилань. Сподіваюся цей урок буде Вам корисний. А зараз давайте прощатися.

Всього Вам доброго і вдалого кодування!