Поиск по сайту:

Как сгенерировать идентификатор ресурса с контрольной суммой


Автор выбрал программу Write for DOnations.

Введение

Уникальные идентификаторы (UID) или идентификаторы могут быть строковым значением или целым числом, и разработчики API часто используют их для обращения к уникальным ресурсам в API. Затем потребители API используют эти идентификаторы для получения одного ресурса из набора ресурсов. Без уникального идентификатора разделить ресурсы и вызывать их по мере необходимости практически невозможно. Идентификаторы могут ссылаться на структурные элементы базы данных, такие как имена таблиц, поля (столбцы) в таблице или ограничения, и могут быть дополнительно указаны для уникального элемента в базе данных. Например, в базе данных, связанной с порталом бронирования отелей, Hotel(id) может указывать на идентификатор, относящийся к уникальному отелю. С помощью Hotel(id=1234, name=Hyatt) вы сможете идентифицировать конкретный отель по идентификатору 1234 или по названию Hyatt .

В Шаблонах проектирования API Джон Дж. Гивакс выделяет семь основных характеристик хорошего идентификатора. Эти характеристики важно учитывать при создании уникального идентификатора:

  • Простота в использовании: в идентификаторе следует избегать зарезервированных символов, таких как косая черта (/), поскольку эти символы имеют определенное значение в URL-адресах.
  • Уникальный: идентификатор должен ссылаться на один ресурс в API.
  • Быстрое создание: процесс создания идентификатора должен выполняться предсказуемым образом для согласованности при масштабировании.
  • Непредсказуемость: непредсказуемость идентификатора обеспечивает преимущества безопасности при управлении уязвимостями.
  • Читаемый: идентификатор должен быть удобочитаемым для человека, что достигается за счет отсутствия цифры 1, нижнего регистра L, верхнего регистра I или символ вертикальной черты (|), так как эти символы могут создать путаницу, если кому-то потребуется проверить идентификатор вручную.
  • Поддающийся проверке: символ контрольной суммы может использоваться для проверки идентификатора во время проверки целостности.
  • Постоянные: после назначения идентификаторы не должны изменяться.

Примечание. Изменение идентификатора может привести к неожиданной путанице. Если у вас есть идентификатор, который указывает Hotel(id=1234, name=Hyatt) и позже меняется на Hotel(id=5678, name=Hyatt) , предыдущий идентификатор может быть доступен для повторного использования. Если предыдущий идентификатор доступен и новый отель создается как Hotel(id=1234, name=Grand Villa), этот новый отель повторно использует исходный идентификатор (1234 ). Затем, когда вы запрашиваете отель 1234, вы можете получить результаты, отличные от ожидаемых.

В этом руководстве вы создадите уникальный настраиваемый идентификатор ресурса, отвечающий этим характеристикам, и соответствующую контрольную сумму, используя Node.JS. Хэш-функция цифрового объекта. Контрольная сумма этого руководства будет представлять собой один буквенно-цифровой символ, полученный в результате алгоритмического процесса кодирования (или хеширования) размера байтов, соответствующего вашему ресурсу.

Предпосылки

Прежде чем приступить к этому руководству, вам потребуется следующее:

  • На вашем компьютере установлен Node.js, который можно настроить, следуя инструкциям по установке Node.js. Это руководство было протестировано с Node.JS версии 16.16.0.
  • Знакомство с Node.js. Узнайте больше в серии статей Как программировать в Node.js.
  • Знакомство с API. Подробное руководство по работе с API можно найти в разделе Как использовать веб-API в Python3. Хотя статья написана для Python, она поможет вам понять основные принципы работы с API.
  • Текстовый редактор, поддерживающий подсветку синтаксиса JavaScript, например Sublime Text. В этом руководстве используется редактор командной строки nano.

Шаг 1 — Создание закодированного идентификатора

На этом шаге вы напишете функцию для генерации идентификатора из случайных байтов в уникальную буквенно-цифровую строку. Ваш идентификатор будет закодирован с использованием кодировки base32, но к нему не будет привязана контрольная сумма до тех пор, пока далее в руководстве. Процесс кодирования создаст уникальный идентификатор указанной длины на основе выбранного вами количества байтов, создав идентификатор, который включает в себя некоторые характеристики хорошего идентификатора.

Начните с создания новой папки для этого проекта, затем перейдите в эту папку:

  1. mkdir checksum
  2. cd checksum

В этом руководстве папка проекта будет называться checksum.

Создайте и откройте файл package.json в папке вашего проекта (используя ваш любимый редактор):

  1. nano package.json

Затем добавьте следующие строки кода:

{
  "name": "checksum",
  "version": "1.0.0",
  "main": "index.js",
  "type": "module"
}

В этом файле вы определяете имя проекта как контрольная сумма и считаете версию кода 1.0.0. Вы определяете основной файл JavaScript как index.js. Если у вас есть type: module в файле package.json, ваш исходный код должен использовать Как работать с JSON в JavaScript.

Сохраните и закройте файл.

Вы будете использовать несколько модулей Node.js для создания идентификатора: base32-decode. Модуль crypto входит в состав Node.JS, но вам потребуется установить base32-encode и base32-decode, чтобы использовать их позже в этом руководстве. . Кодирование — это помещение последовательности символов (буквы, цифры, знаки препинания и определенные символы) в специальный формат для эффективной передачи или хранения. Декодирование — это обратный процесс: преобразование закодированного формата обратно в исходную последовательность символов. Кодировка Base32 использует набор из 32 символов, что делает ее текстовой 32-символьной нотацией для выражения чисел.

В терминальном сеансе установите эти пакеты модулей в папку проекта с помощью следующей команды:

  1. npm i base32-encode base32-decode

Вы получите вывод, указывающий, что эти модули были добавлены:

Output
added 3 packages, and audited 5 packages in 2s found 0 vulnerabilities

Если у вас возникнут проблемы во время установки, вы можете обратиться за помощью к разделу «Как использовать модули Node.js с npm и package.json».

Находясь в папке проекта, создайте новый файл с именем index.js:

  1. nano index.js

Добавьте следующие строки кода JavaScript в файл index.js:

import crypto from 'crypto';  
import base32Encode from 'base32-encode';
import base32Decode from 'base32-decode';
 
function generate_Id(byte_size) {
    const bytes = crypto.randomBytes(byte_size);
    return base32Encode(bytes, 'Crockford');
}

console.log('ID for byte size = 1:',generate_Id(1), '\n');
console.log('ID for byte size = 12:',generate_Id(12), '\n');
console.log('ID for byte size = 123:',generate_Id(123), '\n');

Команда import загружает необходимые модули. Чтобы сгенерировать байты из числа, вы определяете функцию generate_Id, чтобы получить размер байтов в байтах, а затем создать случайные байты этого размера с помощью функции randomBytes из крипто модуль. Затем функция generate_Id кодирует эти байты, используя реализацию Crockford для кодирования base32.

В учебных целях создается несколько идентификаторов, которые затем регистрируются в консоли. Модуль base32-decode будет использоваться для декодирования идентификатора ресурса на следующих шагах.

Сохраните файл index.js, затем запустите код в сеансе терминала с помощью этой команды:

node index.js

Вы получите выходной ответ, подобный этому:

Output
ID for byte size = 1: Y8 ID for byte size = 12: JTGSEMQH2YZFD3H35HJ0 ID for byte size = 123: QW2E2KJKM8QZ7174DDB1Q3JMEKV7328EE8T79V1KG0TEAE67DEGG1XS4AR57FPCYTS24J0ZRR3E6TKM28AM8FYZ2AZTZ55C9VVQTABE0R7QRH7QBY7V3GBYBNN5D9JK0QMD9NXSWZN95S0772DHN43Q003G0QNTPA2J3AFA3P7Q167C1VNR92Z85PCDXCMEY0M7WA

Значения вашего идентификатора могут отличаться из-за случайности сгенерированных байтов. Сгенерированный идентификатор может быть короче или длиннее, в зависимости от выбранного вами размера байта.

Вернувшись в index.js, закомментируйте выходные данные консоли, используя функцию комментирования JavaScript (добавив двойную косую черту // перед строкой):

...
//console.log('ID for byte size = 1:',generate_Id(1), '\n'); 
//console.log('ID for byte size = 12:',generate_Id(12), '\n');
//console.log('ID for byte size = 123:',generate_Id(123), '\n');

Эти строки демонстрируют, как кодирование будет выводить разные идентификаторы на основе связанных байтов. Поскольку эти строки не будут использоваться в следующих разделах, их можно закомментировать, как показано в этом блоке кода, или полностью удалить.

На этом шаге вы создали закодированный идентификатор, кодируя случайные байты. На следующем шаге вы объедините закодированные байты и контрольную сумму, создав уникальный идентификатор.

Шаг 2 — Генерация идентификатора ресурса

Теперь вы создадите идентификатор с символом контрольной суммы. Генерация символа контрольной суммы представляет собой двухэтапный процесс. В учебных целях каждая функция, создающая составную функцию, будет построена отдельно в следующих подразделах. Сначала вы напишете функцию, которая выполняет операцию по модулю. Затем вы напишете еще одну функцию, которая сопоставляет результаты с символом контрольной суммы, и таким образом вы сгенерируете контрольную сумму для своего идентификатора ресурса. Наконец, вы проверите идентификатор и контрольную сумму, чтобы убедиться, что идентификатор ресурса точен.

Запуск операции по модулю

В этом разделе вы преобразуете байты, соответствующие идентификатору числа, в число от 0 до 36 (включительно, что означает любое число от 0 до 36, включая 0 и 36). Байты, соответствующие идентификатору числа, преобразуются в целое число в результате значений BigInteger (BigInt).

Чтобы реализовать эту процедуру, добавьте следующие строки кода в конец файла index.js:

...

function calculate_checksum(bytes) {
    const intValue = BigInt(`0x${bytes.toString('hex')}`);
    return Number(intValue % BigInt(37));
}

Функция calculate_checksum работает с байтами, определенными ранее в файле. Эта функция преобразует байты в шестнадцатеричные значения, которые далее преобразуются в значения BigInteger BigInt. Тип данных BigInt представляет числа, большие, чем числа, представленные примитивным типом данных number в Javascript. Например, хотя целое число 37 относительно мало, оно преобразуется в BigInt для операции по модулю.

Чтобы добиться этого преобразования, вы сначала устанавливаете переменную intValue с методом преобразования BigInt, используя метод toString для установки bytes в hex. Затем вы возвращаете числовое значение с помощью конструктора Number, в котором вы запускаете операцию по модулю с символом %, чтобы найти остаток между intValue и BigInt, используя примерное значение 37. Это целочисленное значение (в данном примере 37) действует как индекс для выбора буквенно-цифрового символа из пользовательской строки буквенно-цифровых символов.

Если значение intValue равно 123 (в зависимости от байтов), операция модуля будет 123 % 37. Результатом этой операции с 37 в качестве целочисленного значения будет остаток от 12 и частное от 3. При значении 154 для идентификатора ресурса операция 154 % 37 приведет к остатку 6.

Эта функция сопоставляет входящие байты с результатом по модулю. Далее вы напишете функцию для сопоставления результата по модулю с символом контрольной суммы.

Получение символа контрольной суммы

После получения результата по модулю в предыдущем разделе вы можете сопоставить его с символом контрольной суммы.

Добавьте следующие строки кода в файл index.js чуть ниже предыдущего кода:

...

function get_checksum_character(checksumValue) {
    const alphabet = '0123456789ABCDEFG' +
        'HJKMNPQRSTVWXYZ*~$=U';  
    return alphabet[Math.abs(checksumValue)]; // 
}

Для функции get_checksum_character вы вызываете checksumValue в качестве параметра. В этой функции вы определяете строковую константу с именем alphabet как пользовательскую строку буквенно-цифровых символов. В зависимости от значения, установленного для checksumValue, эта функция вернет значение, которое связывает определенную строку из константы alphabet с абсолютным значением checksumValue.

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

Добавьте следующие строки кода в файл index.js:

... 

function generate_Id_with_checksum(bytes_size) {
    const bytes = crypto.randomBytes(bytes_size);
    const checksum = calculate_checksum(bytes);
    const checksumChar = get_checksum_character(checksum);
    console.log("checksum character: ", checksumChar); 
    const encoded = base32Encode(bytes, 'Crockford');
    return encoded + checksumChar;
}

const Hotel_resource_id =generate_Id_with_checksum(132)
console.log("Hotel resource id: ",Hotel_resource_id)

Этот раздел кода объединяет две ваши предыдущие функции, calculate_checksum и get_checksum_character (которые используются для генерации символов контрольной суммы), с функцией кодирования в новую функцию с метким названием generate_Id_with_checksum, который создаст идентификатор с символом контрольной суммы.

Сохраните файл, затем запустите код в отдельном сеансе терминала:

  1. node index.js

Вы получите вывод, подобный этому:

Output
checksum character: B Hotel resource id: 9V99B9P55K7M4DN5XYP4VTJYJGENZKJ0F9Q6EEEZ07X49G0V14AXJS3RYXBT3J1WJZXWGM76C6H7G895TJT27AW77BHBX2D16QNQ2ZNBY9MQHWG9NJ1WWVTNRCKRBX6HC3M7BB3JG0V413VJ767JN6FT0GFS5VQJ9X7KSP1KM29B02NAGXN3FP30WA8Y63N1XJAMGDPEE1RNHRTWH6P0B

Тот же символ контрольной суммы появляется в конце идентификатора, указывая на совпадение контрольных сумм.

Эта схематическая диаграмма обеспечивает структурное представление того, как работает эта составная функция:

Эта блок-схема демонстрирует, как идентификатор продукта, который является идентификатором, созданным счетчиком вручную для ресурса, преобразуется в уникальный идентификатор ресурса посредством процесса кодирования и модуля. crypto method на диаграмме относится к функции crypto.randomBytes().

Вы создали идентификатор на основе размера байта, который будет включать символ контрольной суммы. В следующем разделе вы реализуете функцию identifier для проверки целостности идентификатора с помощью декодирования base32.

Проверка целостности идентификатора

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

Добавьте эти строки в файл index.js:

...
function verify_Id(identifier) {
    const value = identifier.substring( 0, identifier.length-1);
    const checksum_char = identifier[identifier.length-1];     
    const buffer = Buffer.from( base32Decode(value, 'Crockford'));
    const calculated_checksum_char = get_checksum_character(calculate_checksum(buffer));
    console.log(calculated_checksum_char);
    const flag =calculated_checksum_char== checksum_char;
    return (flag);    
     }
console.log('\n');
console.log("computing checksum")
const flag = verify_Id(Hotel_resource_id);
if (flag) console.log("Checksums matched.");
else console.log("Checksums did not match.");

Функция verify_Id проверяет целостность идентификатора, проверяя контрольную сумму. Остальные символы идентификатора декодируются в буфер, а затем calculate_checksum и get_checksum_character последовательно запускаются в этом буфере, чтобы извлечь символ контрольной суммы для сравнение (с calculated_checksum_char== checksum_char).

Эта схематическая диаграмма демонстрирует, как работает составная функция:

На этой диаграмме под нарезкой понимается отделение значения идентификатора (value) от символа контрольной суммы (checksum). В предыдущем блоке кода функция identifier.substring( 0, идентификатор.length-1) получает значение идентификатора, тогда как identifier[identifier.length-1] берет последний символ из идентификатора ресурса.

Теперь ваш файл index.js должен соответствовать следующему коду:

import crypto from 'crypto';  // for generating bytes from the number
import base32Encode from 'base32-encode'; // for encoding the bytes into Unique ID as string type
import base32Decode from 'base32-decode';// for decoding the ID into bytes

function generate_Id(byte_size) {
    const bytes = crypto.randomBytes(byte_size);
    return base32Encode(bytes, 'Crockford');
}

//console.log('ID for byte size = 1:',generate_Id(1), '\n');
//console.log('ID for byte size = 12:',generate_Id(12), '\n');
//console.log('ID for byte size = 123:',generate_Id(123), '\n');

function calculate_checksum(bytes) {
    const intValue = BigInt(`0x${bytes.toString('hex')}`);
    return Number(intValue % BigInt(37));
}

function get_checksum_character(checksumValue) {
    const alphabet = '0123456789ABCDEFG' +
        'HJKMNPQRSTVWXYZ*~$=U'; // custom-built string  consisting of alphanumeric character
    return alphabet[Math.abs(checksumValue)]; // picking out an alphanumeric character
}

function generate_Id_with_checksum(bytes_size) {
    const bytes = crypto.randomBytes(bytes_size);
    const checksum = calculate_checksum(bytes);
    const checksumChar = get_checksum_character(checksum);
    console.log("checksum character: ", checksumChar); 
    const encoded = base32Encode(bytes, 'Crockford');
    return encoded + checksumChar;
}

const Hotel_resource_id =generate_Id_with_checksum(132)
console.log("Hotel resource id: ",Hotel_resource_id)

function verify_Id(identifier) {
    const value = identifier.substring( 0, identifier.length-1);
    const checksum_char = identifier[identifier.length-1]; 
    //console.log(value,checksum_char);
    const buffer = Buffer.from( base32Decode(value, 'Crockford'));
    const calculated_checksum_char = get_checksum_character(calculate_checksum(buffer));
    console.log(calculated_checksum_char);
    const flag =calculated_checksum_char== checksum_char;

    return (flag);
    
     }

console.log('\n');
console.log("computing checksum")
const flag = verify_Id(Hotel_resource_id);
if (flag) console.log("Checksums matched.");
else console.log("Checksums did not match.");

Теперь вы можете запустить этот код:

node index.js

Вы получите следующий вывод:

Output
... computing checksum AW75SY7FVC7TKT7VP5ZF0M8C67CN36YZK27BXHVFHSDXJFKH54HK2AXQFMPN89Q5YQRPGNHGAYQ5JFKVD40EKTXCET97Q0FEPX6MX1ZTNWGCA08SBRSHP8B0037ACJG6F6472FEVARCAWM6P5MRJ2F6WTRPXHYS9N1JEDZVH41D33RA5365VNFC5G5VYEFPFJJD8151B28XXDBRHAF80 H H Checksums matched.

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

(Необязательно) Шаг 3 — Изменение идентификатора для несовпадающего результата

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

В файле index.js измените Hotel_resource_id, добавив выделенные строки:

...
const altered_Hotel_resource_id= Hotel_resource_id.replace('P','H');   
console.log("computing checksum")
const flag = verify_Id(altered_Hotel_resource_id);
if (flag) console.log("Checksum matched.");
else console.log("Checksums did not match.");

В приведенном выше коде вы заменяете любой P на H в идентификаторе и переименовываете переменную с Hotel_resource_ID на altered_Hotel_resource_id. . Опять же, эти изменения предназначены для информационных целей и могут быть отменены в конце этого шага для обеспечения целостности соответствия.

Сохраните файл, затем повторно запустите код с изменениями для идентификатора ресурса:

  1. node index.js

Вы получите вывод, что контрольные суммы не совпадают:

Output
Checksums did not match.

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

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

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

Заключение

В этом руководстве вы разработали идентификаторы ресурсов, соответствующие характеристикам хорошего идентификатора. Вы также создали уникальный идентификатор ресурса с контрольной суммой в среде Node.js, используя кодировку base32. Наконец, вы проверили целостность идентификатора, расшифровав его с помощью base32-decoding.

Для взаимного подтверждения вы можете сравнить свои окончательные файлы с файлами из серии «Введение в GitHub» и «Проекты с открытым исходным кодом».

Теперь, когда вы понимаете основы контрольной суммы, вы можете поэкспериментировать с другими алгоритмами кодирования, такими как MD5.