Как создать непоследовательные уникальные идентификаторы на основе числового значения (в стиле YouTube или URL Shortener) в Symfony 3

Если в вашей базе данных есть какая-то таблица, первичным ключом которой является число, обычно в Symfony с представлением CRUD, вы можете увидеть этот регистр в браузере с неким URL-адресом, который показывает идентификатор в URL-адресе, например, http://project/users/12, Чтобы это было легко понять, давайте представим, что у вас есть какая-то веб-система без защиты (без пользовательской системы или какой-либо защиты ролей). В этой системе вы можете создать свой личный профиль и сохранить информацию, которая никто не должен видеть. Ваш профиль будет доступен по адресу:

http://imaginarysystem.com/profiles/2

В этом случае ваш профиль идентифицируется номером 2 (ваш идентификатор в пользовательской таблице приложения равен 2), и только при доступе к этому URL ваша информация становится доступной. Теперь, что если вы измените номер профиля с 2 на 123:

http://imaginarysystem.com/profiles/123

Тогда вы сможете увидеть информацию о пользователе № 123! Что совсем не хорошо, потому что в нашей системе профиль не должен быть доступным для кого-либо еще. Очевидно, что нормальная система должна обеспечивать безопасность, чтобы видеть, является ли пользователь, который получает доступ к профилю, тем же, что и владелец, однако это только пример, который вы можете понять. Теперь было бы неплохо использовать буквенно-цифровую строку для идентификации идентификатора в вашей системе (по крайней мере, его не так просто читать и создавать для людей), например:

http://imaginarysystem.com/profiles/xzq85hj

Эта буквенно-цифровая строка в некотором роде та же самая цифра (2), но «замаскирована», тогда ее можно «разоблачить», когда пользователь вызывает URL как http://imaginarysystem.com/profiles/xzq85hj где xzq85hj должен быть преобразован нашим скриптом в 2 (поскольку каждая база данных выполняет запросы очень быстро с числом вместо строк). Обратите внимание, что вы не должны использовать эту библиотеку в качестве инструмента безопасности и не кодировать конфиденциальные данные. Это не библиотека шифрования.

За этим стилем следуют YouTube или любые другие виды служб URL Shorteners, такие как TinyURL, Google URL Shortener и другие. Кроме того, он обеспечивает защиту злоумышленников, которые просто хотят скопировать содержимое вашего сайта, перепробовав все возможные числа в URL, например http://imaginarysystem.com/profiles/{the number of the loop here e.g 3,4,5,6},

Реализация

Хотя это звучит довольно просто для реализации, так как нам нужно всего лишь хэшировать число, и все, если вы хотите правильно реализовать его в стиле Symfony, тогда это займет некоторое время. Следуй этим шагам:

1. Установите библиотеку хешей

Первое, что вам нужно сделать, это установить библиотеку hashids в ваш проект Symfony. Hashids — это небольшая PHP-библиотека для генерации идентификаторов YouTube, похожих на числа. Используйте его, когда вы не хотите показывать свои идентификаторы базы данных пользователю.

Чтобы установить эту библиотеку, откройте терминал, перейдите в каталог вашего проекта и выполните следующую команду:

composer require hashids/hashids

Кроме того, вы можете изменить свой composer.json файл и добавить зависимость вручную:

{
"require": {
"hashids/hashids": "^2.0"
},
}

Тогда беги composer install, После завершения установки вы сможете использовать библиотеку в своем проекте. Если вы хотите больше информации об этой библиотеке, пожалуйста, посетите официальный репозиторий в Github здесь.

2. Использование в контроллерах

Библиотека hashids работает следующим образом: вы создаете новый экземпляр Hashids, конструктор которого ожидает строку случайности или энтропии в качестве первого аргумента, на основе этой строки будет сгенерирован ваш хэш, кроме того, вы можете указать, какая длина должна иметь строку, предоставив второй параметр. Смотрите следующий пример маскировки:

encode(1); // jEWOEVpQx3
// Set padding of string to 10 and entropy string "My Other Project"
$hashids = new Hashids('My Other Project', 10);
echo $hashids->encode(1); // NA4ByeBWQp

Затем, если вы хотите преобразовать хеш в его числовое представление (снова строка в число 1):

decode("jEWOEVpQx3")[0]; // 1
// Set padding of string to 10 and entropy string "My Other Project"
$hashids = new Hashids('My Other Project', 10);
echo $hashids->decode("NA4ByeBWQp")[0]; // 1

Обратите внимание, что процесс декодирования возвращает массив, поэтому мы получаем доступ к результату по первому и обычно уникальному индексу.

Теперь, в вашем проекте Symfony, вы, очевидно, не будете создавать новый экземпляр класса, а затем будете писать одинаковую строку энтропии и значение заполнения для каждого контроллера, где он вам нужен. Вместо этого мы рекомендуем вам создать для него новый сервис Symfony. В этом сервисе мы будем обрабатывать библиотеку Hashids и создадим 2 метода: один будет использоваться для создания хеша, а другой преобразует хеш в его числовое представление. Строка энтропии, которую мы будем использовать при генерации маски в сервисе, будет пользовательской строкой, объявленной в вашем parameters.yml file и номер отступа (чтобы определить длину сгенерированной маски), однако вы можете определить эти значения непосредственно в вашем классе, если хотите.

В качестве первого шага мы определим вашу строку энтропии и длину сгенерированного хеша в качестве пользовательских параметров в вашем файле параметров (app/config/parameters.yml) или же в соответствии с лучшими практиками в вашем config.yml файл со следующими идентификаторами:

Важный

Эти значения всегда будут одинаковыми, Вы не можете изменить их, как только вы установите их в своем приложении, в противном случае изменится и маска URL.

# Add 2 new parameters
parameters:
url_hasher_entropy: randomstring
url_hasher_padding: 10

Помните, что вы можете (не обязательно) устанавливать эти параметры в файле с именем parameters.yml.dist, в котором хранится канонический список параметров конфигурации для приложения (узнать больше об этом здесь). После создания новых параметров перейдите к созданию нового сервиса Symfony, в этом случае создайте класс, обозначенный как IdHasher (IdHasher.php) и поместите в него следующий код (измените пространство имен в соответствии с вашим приложением):

Заметка

Это базовая реализация хашидов (кодирование и декодирование), вы можете сделать ее еще более сложной, если хотите или нуждаетесь.

HashIdsInstance = new Hashids($entropyString, $hashPadding);
}
public function encode($number){
return $this->HashIdsInstance->encode($number);
}
public function decode($hash){
$result = $this->HashIdsInstance->decode($hash);
return empty($result) ? null : $result[0];
}
}

В этом примере файл был создан в symfonyproject/src/AppBundle/Services, Обратите внимание, что мы добавим 2 статических параметра в наш сервис. Теперь, когда сервис существует, нам просто нужно зарегистрировать его и добавить ранее созданные пользовательские параметры. Открой services.yml файл вашего проекта и зарегистрируйте сервис:

services:
# Name of the service id_hasher
id_hasher:
# Path of the previously created class
class: AppBundle\Services\IdHasher
# Inject the custom parameters as arguments for the IdHasher class
arguments: ["%url_hasher_entropy%" , "%url_hasher_padding%"]

Наш сервис может потребоваться с id_hasher Идентификатор, сохраните изменения, очистите кеш вашего проекта, и сервис теперь должен быть зарегистрирован, вы можете использовать его из ваших контроллеров, например:

get = the service container
$hasherService = $this->get("id_hasher");
// With our secret generates : bJ0d4Wn3x8 (will vary on your project as the secret is different)
echo $hasherService->encode(1);
// Decode the hash generated by the encode function
echo $hasherService->decode("bJ0d4Wn3x8"); // 1

Довольно легко, верно? Благодаря этому вы сможете генерировать и использовать хеши числовых значений по своему усмотрению, создавать собственные маршруты и т. Д. Если функция декодирования возвращает ноль, это означает, что предоставленный «хэш» не может быть преобразован в действительный число.

Пример в контроллерах

Теперь давайте реализуем то, что вам будет проще понять. Мы создадим VideosControllerэтот контроллер имеет только 2 маршрута, один маршрут преобразует числовой идентификатор, полученный в URL, в его хэш (маршрут процесса) и автоматически перенаправляет в представление show:

get("id_hasher");
// Generate hash of the integer number
$hashId = $hasherService->encode($id);
// Send a response
// http://demo/app_dev.php/videos/generate/1 => generates hashId bJ0d4Wn3x8
// return new Response("The hash for your video with numeric id $id is : ".$hashId);
// Or you can make it interesting and generate a route directly to the showAction (http://demo/app_dev.php/videos/show/{the generated hash})
return $this->redirectToRoute('videos_show', [
'hashId' => $hashId
]);
}
public function showAction($hashId){
// Retrieve our created hasher service
$hasherService = $this->get("id_hasher");
// Convert the hash to its integer representation
$numericId = $hasherService->decode($hashId);
// Send a response
// http://demo/app_dev.php/videos/show/bJ0d4Wn3x8 => prints "1" as numeric id again :)
return new Response("The number of your video with id $hashId is : ".$numericId);
}
}

Маршруты следующего контроллера определены в routing.yml файл со следующей структурой:

videos_generate:
path:      /videos/generate/{id}
defaults:  { _controller: AppBundle:Videos:generate }
videos_show:
path:      /videos/show/{hashId}
defaults:  { _controller: AppBundle:Videos:show }

Этот контроллер работает довольно просто, вы получаете доступ к действию создания с помощью следующего примера URL http://demo/videos/generate/189, Теперь, когда пользователь посещает URL, он будет автоматически перенаправлен в представление show, которое преобразует 189 в его хеш, который в нашем случае был http://demo/videos/show/bJ0d4rzE3x,

То, как вы обрабатываете хэши для генерации маршрутов, перенаправления или решения о сохранении хешей в базе данных (не очень эффективно), полностью зависит от вас.

3. Использование хэшей в Twig

Если вы были мудрыми и решили не сохранять сгенерированные хеши сервисом в базе данных, поскольку это немного ненужно, вы, вероятно, уже подумали, Как мне создать маршруты с хэшем на представлениях? Отличный вопрос! Как видите, до этого момента мы можем генерировать хэши только на контроллере или с помощью PHP-кода в вашем проекте, но не в Twig. Поэтому вам нужно создать новое расширение Twig которые раскрывают новые функции, а именно url_id_hasher_encode а также url_id_hasher_decode (вы можете изменить их имя, если хотите).

Расширение, которое вам нужно создать, действительно простое, нам просто требуется тот же сервис, который был создан ранее (шаг № 2) в расширении Twig, и написать небольшую оболочку для функции кодирования и декодирования в twig. Мы создадим только две функции Twig, которые ожидают в качестве уникального аргумента строку или число для кодирования или декодирования (в зависимости от того, какую функцию вы используете).

Чтобы создать расширение, создайте новый файл, а именно TwigExtensions.php со следующим содержанием:

id_hasher = $hasherService;
}
/**
* Declare encode function
*
* @param $value {Integer || String} Number to convert into hash
*/
public function urlIdHasherEncode($value){
return $this->id_hasher->encode($value);
}
/**
* Declare decode function
*
* @param $value {Integer || String} NHash to convert into number
*/
public function urlIdHasherDecode($value){
return $this->id_hasher->decode($value);
}
public function getName()
{
return 'TwigExtensions';
}
}

Заметка

Если у вас уже есть файл TwigExtensions, то вы можете только скопировать функции для хеширования чисел и наоборот, но не забудьте внедрить созданный сервис в расширение Twig.

Особенностью расширения являются функции кодирования и декодирования PHP, которые зарегистрированы в Twig как url_id_hasher_encode а также url_id_hasher_decode, Как видите, он получает id_hasher service (создается на шаге # 2) и кодирует или декодирует строку в соответствии с ее аргументами. Теперь зарегистрируйте расширение в services.yml файл (измените класс в соответствии с вашим местоположением):

services:
twig.extension:
# the namespace with the name of your twig extension class
class: AppBundle\Extensions\TwigExtensions
arguments:
service_container : "@id_hasher"
tags:
-  { name: twig.extension }

Сохраните изменения, очистите кэш вашего проекта, и теперь функции можно использовать в представлениях Twig, как показано в следующем примере:

{% extends 'base.html.twig' %}
{% block body %}
{# Note that according to your custom parameters the hashes will change#}
{# Prints bJ0d4Wn3x8 #}
{{ url_id_hasher_encode(1)}}
{# Prints 1#}
{{ url_id_hasher_decode("bJ0d4Wn3x8")}}
{% endblock %}

И пример того, как сгенерировать маршрут (с ранее зарегистрированным маршрутом videos_show):

{% extends 'base.html.twig' %}
{% block body %}
{# http://demo/videos/show/bJ0d4rzE3x #}
{% set LinkUrl = path("videos_show", { 'hashId': url_id_hasher_encode(1) }) %}

Redirect to video !

{% endblock %}
Ссылка на основную публикацию
Adblock
detector