如何生成带校验和的资源标识符

2023-11-08

作者选择了免费开源基金接受捐赠作为为捐款而写程序。

介绍

唯一标识符(UID),或身份标识,可以是字符串值或整数,API 开发人员经常使用它们来寻址 API 中的唯一资源。然后,API 使用者使用这些标识符从资源集合中获取单个资源。如果没有唯一的标识符,分离资源并根据需要调用它们几乎是不可能的。标识符可以引用数据库结构元素,例如表的名称、表中的字段(列)或约束,并且可以进一步指定为数据库中的唯一项。例如,在与酒店预订门户相关的数据库中,Hotel(id)可能指向一个引用唯一酒店的标识符。和Hotel(id=1234, name="Hyatt"),您将能够通过 ID 识别该特定酒店1234或名字"Hyatt".

In API设计模式约翰·J·吉瓦克斯 (John J. Gewax) 指出了一个好的标识符的七个基本特征。生成唯一 ID 时需要考虑以下重要特征:

  • 易于使用:标识符应避免保留字符,例如正斜杠 (/) 因为这些字符在 URL 中具有特定的含义。
  • 唯一:标识符应该能够引用 API 中的单个资源。
  • 快速生成:ID 生成过程应以可预测的方式执行,以确保扩展时的一致性。
  • 不可预测:当标识符不可预测时,它可以为漏洞管理提供安全优势。
  • 可读:标识符应该是人类可读的,这是通过避免数字来实现的1, 小写L, 大写I,或管道字符 (|),因为如果有人需要手动检查 ID,这些字符可能会造成混乱。
  • 可验证:checksum字符可用于在完整性检查期间验证 ID。
  • 永久:一旦分配,标识符就不应更改。

Note:更改标识符可能会造成意外的混乱。如果您有一个指定的标识符Hotel(id=1234, name="Hyatt")后来变成Hotel(id=5678, name="Hyatt"),先前的 ID 可能可以重复使用。如果先前的标识符可用并且新酒店被创建为Hotel(id=1234, name="Grand Villa"),这家新酒店重新使用了原来的标识符(1234)。然后当你问酒店的时候1234,您可能会收到与预期不同的结果。

在本教程中,您将使用 Node.JS 生成满足这些特征的唯一自定义资源标识符以及关联的校验和。 Achecksum是使用文件或数字数据获得的数字指纹的哈希值哈希函数在数字对象上。本教程的校验和将是通过对与您的资源相对应的字节大小进行编码(或散列)的算法过程派生的单个字母数字字符。

先决条件

在开始本教程之前,您需要以下内容:

  • Node.js 安装在您的计算机上,您可以通过以下方式进行设置如何安装 Node.js。本教程已使用 Node.JS 版本 16.16.0 进行了测试。
  • 熟悉 Node.js。了解更多信息如何在 Node.js 中编码 series.
  • 熟悉 API。有关使用 API 的综合教程,您可以查看如何在 Python3 中使用 Web API。虽然本文是针对 Python 编写的,但它将帮助您了解使用 API 的核心概念。
  • 支持JavaScript语法高亮的文本编辑器,例如Atom, 视觉工作室代码, or 崇高的文字。本教程使用命令行编辑器nano.

第 1 步 — 生成编码 ID

在此步骤中,您将编写一个函数,将随机字节生成标识符到唯一的字母数字字符串中。您的标识符将使用以下方式进行编码Base32编码,但直到本教程后面才会有附加的校验和。编码过程将根据您选择的字节数创建指定长度的唯一标识符,从而构建一个包含良好 ID 的一些特征的 ID。

首先为此项目创建一个新文件夹,然后移至该文件夹:

  1. mkdir checksum
  2. cd checksum

The project folder will be called checksum for this tutorial.

创建并打开一个package.json文件在您的项目文件夹中(使用您最喜欢的编辑器):

  1. nano包.json

然后添加以下代码行:

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

In this file, you define the project name as checksum, and you consider the code version "1.0.0". You define the main JavaScript file as index.js. When you have "type": "module" in the package.json file, your source code should use import syntax. In this file, you use the JSON data format, which you can learn more about in How to Work with JSON in JavaScript.

保存并关闭文件。

您将使用一些 Node.js 模块来生成 ID:crypto and base32-encode,及其相应的解码器base32-decode. The crypto模块与 Node.JS 一起打包,但您需要安装base32-encode and base32-decode供本教程后面使用。编码是将一系列字符(字母、数字、标点符号和某些符号)转换为专用格式,以便有效传输或存储。解码是相反的过程:将编码格式转换回原始字符序列。 Base32 编码使用 32 个字符集,这使其成为表达数字的文本 32 符号表示法。

在终端会话中,使用以下命令在项目文件夹中安装这些模块包:

  1. npm我的base32编码base32解码

您将收到一个输出,表明这些模块已添加:

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

如果您在安装过程中遇到问题,可以参考如何将 Node.js 模块与 npm 和 package.json 一起使用为了支持。

仍然在您的项目文件夹中,创建一个名为的新文件index.js:

  1. nano索引.js

将以下 JavaScript 代码行添加到index.js file:

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');

The import命令加载所需的模块。要从数字生成字节,您需要定义一个generate_Id函数获取字节的大小,然后使用以下命令创建该大小的随机字节randomBytes函数从crypto模块。这generate_Id然后函数使用以下方法对这些字节进行编码克罗克福德Base32编码的实现。

出于教学目的,会生成一些 ID,然后将其记录到控制台。这base32-decode模块将用于在接下来的步骤中解码资源 ID。

保存你的index.js文件,然后使用以下命令在终端会话中运行代码:

node index.js

您将收到类似于以下内容的输出响应:

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

由于生成字节的随机性,您的 ID 值可能会有所不同。生成的 ID 的长度可能较短或较长,具体取决于您选择的字节大小。

Back in index.js,使用 JavaScript 注释功能注释掉控制台输出(添加双斜杠//行前):

index.js
...
//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');

这些行演示了编码如何根据关联的字节输出不同的标识符。由于这些行不会在以下部分中使用,因此您可以如本代码块中所示注释掉它们或完全删除它们。

在此步骤中,您通过对随机字节进行编码来创建编码 ID。在下一步中,您将组合编码字节和校验和,创建唯一标识符。

第 2 步 — 生成资源标识符

现在您将创建一个带有校验和字符的 ID。生成校验和字符是一个两步过程。出于教学目的,创建复合函数的每个函数将在以下小节中单独构建。首先,您将编写一个运行模运算。然后,您将编写另一个函数,将结果映射到校验和字符,这就是您为资源 ID 生成校验和的方式。最后,您将验证标识符和校验和,以确保资源标识符准确。

运行模运算

在本节中,您将把数字 ID 对应的字节转换为 0-36 之间的数字(包含限制,这意味着介于 0 和 36 之间的任何数字)0 to 36, 包括0 and 36)。与数字 ID 对应的字节被转换为整数,作为 a 的结果模运算。模运算将返回通过将字节转换为而获得的被除数的余数大整数 (BigInt)值。

要实现此过程,请将以下代码行添加到index.js file:

index.js
...

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

功能calculate_checksum使用文件中前面定义的字节。该函数将字节转换为十六进制值,并进一步转换为大整数 BigInt价值观。这BigInt数据类型表示的数字大于原始数据类型表示的数字number in Javascript。例如,虽然整数37比较小,转换为BigInt用于模运算。

要实现此转换,您首先设置intValue变量与BigInt转换方法,使用toString设定方法bytes to hex。然后,您返回一个数值Number构造函数,在其中运行模运算%符号来查找之间的余数intValue and BigInt使用样本值37。该整数值(在本例中,37) 充当索引,从定制的字母数字字符字符串中选择字母数字字符。

If intValue值为123(取决于bytes),模块操作将是123 % 37。此操作的结果是37因为整数值将是以下的余数12和一个商3。值为154对于资源 ID,操作154 % 37将导致剩余的6.

该函数将传入字节映射到模结果。接下来,您将编写一个函数将模结果映射到校验和字符。

获取校验和字符

在获得上一节中的模结果后,您可以将其映射到校验和字符。

将以下代码行添加到index.js文件就在前面的代码下面:

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.

接下来,您将编写一个函数,该函数使用这些部分中编写的两个函数,根据字节编码与校验和字符相结合来生成 ID。

将以下代码行添加到index.js file:

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 and get_checksum_character(用于生成校验和字符),将编码函数转换为一个适当命名的新函数generate_Id_with_checksum这将创建一个带有校验和字符的 ID。

保存文件,然后在单独的终端会话中运行代码:

  1. node索引.js

您将收到与此类似的输出:

Output
checksum character: B Hotel resource id: 9V99B9P55K7M4DN5XYP4VTJYJGENZKJ0F9Q6EEEZ07X49G0V14AXJS3RYXBT3J1WJZXWGM76C6H7G895TJT27AW77BHBX2D16QNQ2ZNBY9MQHWG9NJ1WWVTNRCKRBX6HC3M7BB3JG0V413VJ767JN6FT0GFS5VQJ9X7KSP1KM29B02NAGXN3FP30WA8Y63N1XJAMGDPEE1RNHRTWH6P0B

相同的校验和字符出现在 ID 的末尾,表明校验和匹配。

此示意图提供了此复合函数如何工作的结构表示:

A chart with Product ID at the top. It points to Crypto method, which points to Bytes. There are two branches from Bytes: base32-decode and Modulo process. The base32-decode branch points to the Encoded ID, whereas the Modulo process branch points to the Checksum. When the Encoded ID and Checksum are paired, they become the Resource ID.
Generating the ID

此流程图演示了产品 ID(由资源计数器手动创建的标识符)如何通过编码和取模过程转换为唯一的资源 ID。这crypto method图中指的是crypto.randomBytes()功能。

您根据包含校验和字符的字节大小创建了一个 ID。在下一节中,您将实现一个identifier验证 ID 完整性的函数base32-decoding.

检查标识符的完整性

为了确保完整性,您现在将使用名为的新函数将校验和字符(标识符的最后一个字符)与生成的校验和进行比较verify_Id。比较校验和字符是检查原始 ID 完整性并确定其未被篡改的重要步骤。

将这些行添加到您的index.js file:

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.");

The verify_Id函数通过检查校验和来检查 ID 的完整性。标识符的其余字符被解码为buffer, 进而calculate_checksum and get_checksum_character随后在此缓冲区上运行以提取用于比较的校验和字符(与calculated_checksum_char== checksum_char).

该示意图演示了复合函数的工作原理:

A chart with Resource ID at the top. It points to Slicing Method, which has two branches: Value and Checkum. The Value branch points to the base32-decode, which then becomes a Decoded checksum. The Checksum branch points to a Checksum. If the Decoded checksum and the Checksum match, it results in Verification.
Verifying the ID

在此图表中,slicing指的是分隔ID值(value) 来自校验和字符 (checksum)。在您之前的代码块中,该函数identifier.substring( 0, identifier.length-1)获取 ID 值,而identifier[identifier.length-1]获取资源 ID 中的最后一个字符。

Your index.js文件现在应该与以下代码匹配:

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使用校验和字符检查标识符的完整性。接下来,出于指导目的,您可以更改资源 ID,以便该函数给出不匹配的结果,以评估检查失败时会发生什么情况。

(可选)步骤 3 — 更改不匹配结果的标识符

您现在将更改标识符的值以检查校验和是否匹配。此步骤中的更改将始终导致校验和不匹配,因为如果对 ID 中的任何字符进行操作,则无法保持完整性。此类更改可能是由于传输错误或恶意行为造成的。此更改仅用于指导目的,不建议用于生产版本,但将使您能够评估不匹配的校验和结果。

In your index.js文件,修改Hotel_resource_id通过添加突出显示的行:

index.js
...
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 with H在 ID 中并将变量重命名为Hotel_resource_ID to altered_Hotel_resource_id。同样,这些更改仅供参考,可以在此步骤结束时恢复以确保匹配完整性。

保存文件,然后重新运行代码并更改资源 ID:

  1. node索引.js

您将收到校验和不匹配的输出:

Output
Checksums did not match.

在此步骤中,您创建了一个函数来验证校验和是否通过完整性测试,并且您遇到了这两种情况。不匹配的校验和表明资源 ID 已被操纵。该通知使开发人员能够根据应用程序的要求对恶意行为采取行动,例如阻止用户请求或报告与资源 ID 相关的请求。

要将函数恢复为匹配的校验和结果,请删除在此步骤开始时添加的附加代码,以便该代码与步骤 2 末尾的文件匹配。

当您需要带有校验和的自定义唯一 ID 时,您可以使用本教程来帮助您生成数据模型、版本化 API 等。

结论

在本教程中,您开发了符合良好标识符特征的资源 ID。您还在 Node.js 环境中使用以下命令创建了带有校验和的唯一资源 IDbase32-encoding。最后,您通过使用解码来验证 ID 的完整性base32-decoding.

为了交叉确认,您可以将最终文件与中的文件进行比较DigitalOcean 社区存储库。你也可以git-clone如果您熟悉存储库git版本控制系统或遵循GitHub 和开源项目简介 series.

现在您已经了解了校验和的基础知识,您可以尝试其他编码算法,例如MD5.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何生成带校验和的资源标识符 的相关文章