API 登录

如需直接访问 zeuz 基础 API,您必须编写脚本来登录 zeuz 并生成请求哈希,进而用请求哈希来创建会话密钥并附到您的 API 请求上。会话密钥的有效期为 24 小时。

下列步骤和 C++ 代码示例向您展示了登录脚本中必须包含的内容,其中涵盖了 API 认证 中的任务 3. 登录 zeuz 并生成会话密钥

关于 API 认证 中,涵盖任务 3. 登录 zeuz 并生成会话密钥 和任务 4. 生成签名哈希并附在 API 请求上 的完整工作示例,请参阅 示例代码

警告:登录脚本不是必须的。

请参阅下方的 我是否需要登录脚本?

资讯:我是否需要登录脚本?

在开始之前,请参阅 API 简介,检查您是否可以使用 SDK 下载中提供的封装之一或 Go SDK 来访问 zeuz API。如果您可以使用封装或 Go SDK,就不需要自己编写认证脚本。

在下列情况下,您必须直接访问 zeuz 基础 API:

  • 您不使用 Unity、虚幻引擎或 Go 开发游戏。
  • 您想调用 zeuz API 为您的游戏建立工具,例如,将您的游戏集成到 CI/CD 管道中。

前期准备

在开始创建登录脚本之前,请确保您已经:

  • 阅读 zeuz API 的介绍信息。

    请参阅 API 简介

  • 阅读 zeuz 基础 API 的认证过程,了解需要完成哪些设置。

    请参阅 API 认证

  • 有权访问与您的 zeuz 账号相关联的开发人员资料。

    • 根据端点的授权级别,使用开发人员资料或 API 密钥来发送 API 请求。

      详情请参阅 授权

    • 使用开发人员资料来创建 API 密钥。

      如果您无法访问与 zeuz 账号相关联的开发人员资料,请创建一个开发人员资料。

      详情请参阅 开发人员

  • 生成 API 密钥 (取决于您访问 API 的方式)。

    如果您想从您的游戏或游戏以外的服务中调用 zeuz 基础 API (例如,管理负载),请生成一个 API 密钥。

    我们建议您为要执行的每项任务单独生成一个 API 密钥。

    详情请参阅 API 密钥

  • 检查本地开发机上的时钟设置是否正确。

    如果时钟设置不正确,认证可能会失败并返回 request expired 错误。

    关于错误处理的示例,请参阅 步骤4 - 调用 auth_login 端点 中的代码。

获得开发人员的登录信息和 / 或 API 密钥和 API 密码之后,请在登录脚本中执行下列步骤:

步骤 1 - 生成 nonce 和时间戳

编写一个函数以生成 nonce (仅供一次性使用的数字) 和时间戳。该函数将用于 步骤 3 - 创建并编码请求哈希

关于 zeuz 时间戳的更多信息,请参阅 时间处理

C++ 代码示例如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <chrono>
#include <string>
#include <unistd.h>

// 生成指定长度的 nonce
std::string generate_nonce(const int len) {
   std::string tmp_s;
   static const char alphanum[] =
      "0123456789"
      "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
      "abcdefghijklmnopqrstuvwxyz";

   srand( (unsigned) time(NULL) * getpid());

   tmp_s.reserve(len);

   for (int i = 0; i < len; ++i) {
       tmp_s += alphanum[rand() % (sizeof(alphanum) - 1)];
   }

   return tmp_s;
}
// 生成当前时间的 zeuz 时间戳
// 更多信息,请参阅:See doc.zeuz.io/docs/time-handling
long zeuz_now() {
   auto now = std::chrono::system_clock::now();
   auto now_ms = std::chrono::time_point_cast<std::chrono::seconds>(now);
   auto epoch = now_ms.time_since_epoch();
   return (epoch.count() + 2208988800) * 1000 * 1000;
}

步骤 2 - 创建并编码密码哈希

编写一个函数,使用您的 zeuz 开发人员用户名和密码或 API 密钥和 API 密码,来生成密码哈希。使用开发人员凭证还是 API 凭证取决于您想调用的 API 端点的授权级别。更多信息,请参阅 授权

该函数 (下列代码示例中的 generate_pwhash) 必须完成下列工作:

  1. 连接 “zeuz” 与您的开发人员用户名或 API 密钥,以创建一个

  2. 将盐与您的开发人员密码或 API 密码一起传递给 脚本 函数以生成脚本哈希值。下列代码示例使用 libscrypt 库。

  3. 使用 Base64 编码脚本哈希。

  4. 连接 “a” 与编码后的脚本哈希,以创建最终的密码哈希。

C++ 代码示例如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <libscrypt.h>

// 使用选中的库执行 Base64 编码。
std::string base64_encode(unsigned char * value, unsigned int length);

// 生成密码哈希。
std::string generate_pwhash(std::string login, std::string password) {
   std::string salt = "zeuz" + login;
   uint8_t hash[32] = { 0 }; // The output hash is 32 bits long

   // 本示例使用 libscrypt-dev 库。
   // 运行 sudo apt-get install libscrypt-dev 命令安装该库。
   libscrypt_scrypt(
       reinterpret_cast<const uint8_t*>(&(password.c_str())[0]),
       password.length(),
       reinterpret_cast<const uint8_t*>(&(salt.c_str())[0]),
       salt.length(),
       1024,
       8,
       1,
       hash,
       32);

   return "a" + base64_encode((unsigned char *) hash, 32);
}

步骤 3 - 创建并编码请求哈希

编写一个函数,将 nonce、时间戳和密码哈希值合并为一个称为请求哈希的哈希值,然后使用 Base64 编码请求哈希。

下列图片使用下方示例代码中的函数名和变量名,概述了请求哈希的创建流程:

图像:请求哈希创建流程

图像:请求哈希创建流程

下方示例代码中使用的函数为 zeuz_hash,该函数将用于 步骤 5 - 创建会话密钥。已使用图例阐明该函数涉及的参数和返回值。

C++ 代码示例如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 使用安全哈希算法 SHA3-256 对输入字符串进行哈希。
void sha3_256_hash(const char * value, int length, unsigned char ** output, unsigned int * output_length);

// 使用 Base64 对输入字符进行编码。
std::string base64_encode(unsigned char * value, unsigned int length);

// 使用安全哈希算法 SHA3-256 对输入字符串进行哈希。
// 使用 Base64 对其进行编码并返回编码后的值。
std::string zeuz_hash(std::string value) {
   unsigned char * hash;
   unsigned int hash_length;
   sha3_256_hash(value.c_str(), value.length(), &hash, &hash_length);
   std::string base64_hash = base64_encode(hash, hash_length);
   return base64_hash;
}

// 生成指定长度的 nonce。
auto nonce = generate_nonce(10);

// 生成当前时间的 zeuz 时间戳。
// See doc.zeuz.io/docs/time-handling for more information
auto now = zeuz_now();

// 生成密码哈希。
auto pwhash = generate_pwhash(login, password);

// 将 nonce、时间戳和密码哈希组成请求哈希。
auto request_hash = zeuz_hash(nonce + std::to_string(now) + pwhash);

步骤 4 - 调用 auth_login 端点

调用 auth_login API 端点,使用上述步骤中的请求哈希登录 zeuz。您必须用 POST 方法把 JSON 格式的登录信息发送给 zeuz。

此外您还必须提供 auth_login 所需的其他属性。关于端点属性的详情,请参阅 API 参考中的 auth_login

zeuz 在您创建开发人员资料和 / 或 API 密钥时生成了一个密码哈希值,并使用该密码哈希与您在 auth_login 中提供的 nonce 和时间戳,创建一个请求哈希。如果该请求哈希值与上述步骤中生成的请求哈希值相匹配,zeuz 会判定登录有效。反之,如果不匹配,zeuz 会拒绝该登录请求。

如果登录成功,zeuz 会向执行登录的函数发送一些信息,其中包含下列内容:

  • 会话 ID:用于识别会话的唯一值。在之后的请求中会用到。

    更多信息,请参阅 API 认证

    注意:之后的请求指的是登录后在同一个登录会话内执行的 API 请求。

  • 会话 nonce:新的仅供一次性使用的随机字符串。

  • ValidThru:会话过期的日期 / 时间。

从您的脚本中取出这些值,用于之后的 API 请求。

C++ 代码示例如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
// 用 JSON 格式化您的登录数据。
Json::Value data;
Json::FastWriter writer;
data["Time"] = Json::Int64(time);
// 请求哈希。
data["Data"]["Hash"] = hash;
data["Data"]["IsApi"] = true;
data["Data"]["IsUser"] = false;
data["Data"]["Login"] = login;
// Nonce。
data["Data"]["Nonce"] = nonce;
// 时间戳。
data["Data"]["Time"] = Json::Int64(time);
auto json = writer.write(data);

// 在 HTTP POST 请求体中向zeuz API发送数据。
// auth_login 端点。
auto response = post_request("https://zcp.zeuz.io/api/v1/auth_login", json);

// 解析 JSON 响应。
Json::Reader reader;
Json::Value response_json;
if (!reader.parse(response, response_json)) {
// 解析失败则报错。
// 注意:在生产代码中,使用 C++ 异常来替代。
   throw std::string("could not parse response data ( " + response + ")");
}

// 检查是否设置错误字段。
auto error = response_json["Error"].asString();
if (!error.empty()) {
   // 如果错误以 request_expired 开头,
   // 说明生成的时间戳已过期。
   if (error.rfind("request_expired", 0) == 0) {
       // 注意:在生产代码中,您可以重试登录,
       // 或确保系统时间是正确的。
       throw std::string("verify system clock or retry request");
   } else {
       // 出现异常错误。认证信息可能有误。
       // 注意:在生产代码中,使用 C++ 异常来替代。
       throw std::string("could not login (" + response + ")");
   }
}

// 提取会话 ID、会话 nonce 和
// 会话到期的时间 (ValidThru)。
// of session expiry (ValidThru)
std::string session_id = response_json["Data"]["SessionId"].asString();
std::string session_nonce = response_json["Data"]["SessionNonce"].asString();
std::string valid_thru = response_json["Data"]["ValidThru"].asInt64();

注意: 在极少数情况下,会话可能提早于 ValidThru 变量指定的日期 / 时间失效。为防止这种情况,请监测请求的响应代码,如会话提前失效,请重新认证。详情请参阅 错误处理

步骤 5 - 创建会话密钥

现在您需要创建一个会话密钥。如需创建会话密钥,请使用您在 步骤 3 中创建的函数 (代码示例中的 zeuz_hash),将您在 步骤 4 中从 zeuz 收到的会话 nonce 与密码哈希结合起来。会话密钥的有效期为 24 小时。

下列图片使用示例代码中的函数名和变量名,概述了回话密钥的创建流程。关于 generate_pwhash 的更多信息,请参阅步骤 2 和步骤 3。

图像:会话密钥创建流程

图像:会话密钥创建流程

C++ 代码示例如下:

1
2
// 将会话 nonce 和密码哈希组合为会话密钥
auto session_key = zeuz_hash(session_nonce + pwhash);

下一步:认证 API 请求

现在您已经登录到 zeuz,您可以生成一个签名哈希,并将其附到您的 API 请求中。

如需生成签名哈希,您必须生成一个新的 nonce 和一个新的时间戳,用它们和会话密钥一起生成另一个称为签名哈希的哈希密码,然后用该签名哈希来认证 API 请求,直至会话密钥过期 (会话密钥在创建后 24 小时过期)。

详情请参阅 签名哈希生成,以及生成签名哈希以认证后续 API 请求的示例。



2021年8月9日 该文档已更新并通过审校:添加创建请求哈希和会话密钥的流程图

2021年5月12日 该文档已创建并通过审校


最近更新时间: October 20, 2021 (e364a72d)