唯一标识符(UID),或身份标识,可以是字符串值或整数,API 开发人员经常使用它们来寻址 API 中的唯一资源。然后,API 使用者使用这些标识符从资源集合中获取单个资源。如果没有唯一的标识符,分离资源并根据需要调用它们几乎是不可能的。标识符可以引用数据库结构元素,例如表的名称、表中的字段(列)或约束,并且可以进一步指定为数据库中的唯一项。例如,在与酒店预订门户相关的数据库中,Hotel(id)可能指向一个引用唯一酒店的标识符。和Hotel(id=1234, name="Hyatt"),您将能够通过 ID 识别该特定酒店1234或名字"Hyatt".
In API设计模式约翰·J·吉瓦克斯 (John J. Gewax) 指出了一个好的标识符的七个基本特征。生成唯一 ID 时需要考虑以下重要特征:
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 符号表示法。
在终端会话中,使用以下命令在项目文件夹中安装这些模块包:
npm我的base32编码base32解码
您将收到一个输出,表明这些模块已添加:
Output
added 3 packages, and audited 5 packages in 2s
found 0 vulnerabilities
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。生成校验和字符是一个两步过程。出于教学目的,创建复合函数的每个函数将在以下小节中单独构建。首先,您将编写一个运行模运算。然后,您将编写另一个函数,将结果映射到校验和字符,这就是您为资源 ID 生成校验和的方式。最后,您将验证标识符和校验和,以确保资源标识符准确。
在本节中,您将把数字 ID 对应的字节转换为 0-36 之间的数字(包含限制,这意味着介于 0 和 36 之间的任何数字)0 to 36, 包括0 and 36)。与数字 ID 对应的字节被转换为整数,作为 a 的结果模运算。模运算将返回通过将字节转换为而获得的被除数的余数大整数 (BigInt)值。
功能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.
这部分代码结合了您之前的两个函数,calculate_checksum and get_checksum_character(用于生成校验和字符),将编码函数转换为一个适当命名的新函数generate_Id_with_checksum这将创建一个带有校验和字符的 ID。
保存文件,然后在单独的终端会话中运行代码:
node索引.js
您将收到与此类似的输出:
Output
checksum character: B
Hotel resource id: 9V99B9P55K7M4DN5XYP4VTJYJGENZKJ0F9Q6EEEZ07X49G0V14AXJS3RYXBT3J1WJZXWGM76C6H7G895TJT27AW77BHBX2D16QNQ2ZNBY9MQHWG9NJ1WWVTNRCKRBX6HC3M7BB3JG0V413VJ767JN6FT0GFS5VQJ9X7KSP1KM29B02NAGXN3FP30WA8Y63N1XJAMGDPEE1RNHRTWH6P0B
为了确保完整性,您现在将使用名为的新函数将校验和字符(标识符的最后一个字符)与生成的校验和进行比较verify_Id。比较校验和字符是检查原始 ID 完整性并确定其未被篡改的重要步骤。
将这些行添加到您的index.js file:
index.js
...functionverify_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).
该示意图演示了复合函数的工作原理:
在此图表中,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 numberimport base32Encode from'base32-encode';// for encoding the bytes into Unique ID as string typeimport base32Decode from'base32-decode';// for decoding the ID into bytesfunctiongenerate_Id(byte_size){const bytes = crypto.randomBytes(byte_size);returnbase32Encode(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');functioncalculate_checksum(bytes){const intValue =BigInt(`0x${bytes.toString('hex')}`);returnNumber(intValue %BigInt(37));}functionget_checksum_character(checksumValue){const alphabet ='0123456789ABCDEFG'+'HJKMNPQRSTVWXYZ*~$=U';// custom-built string consisting of alphanumeric characterreturn alphabet[Math.abs(checksumValue)];// picking out an alphanumeric character}functiongenerate_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)functionverify_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.
您现在将更改标识符的值以检查校验和是否匹配。此步骤中的更改将始终导致校验和不匹配,因为如果对 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:
node索引.js
您将收到校验和不匹配的输出:
Output
Checksums did not match.
在此步骤中,您创建了一个函数来验证校验和是否通过完整性测试,并且您遇到了这两种情况。不匹配的校验和表明资源 ID 已被操纵。该通知使开发人员能够根据应用程序的要求对恶意行为采取行动,例如阻止用户请求或报告与资源 ID 相关的请求。