---
title: 准备向 API 发起已验证的请求
description: 如何构建并签署 JWT、向 OAuth2 平台请求访问令牌，以及处理令牌响应与过期问题。
canonical: https://developer.unico.io/zh-CN/dual-api/developers/regional-solutions/card-not-present-verification/integration/authentication/preparing-authenticated-request
locale: zh-CN
generated_by: markdown-export
---

创建并配置服务账号后，您的应用程序需要完成以下步骤：

1. 创建 JSON Web Token（JWT），其中包含头部（header）、负载（payload）和签名（signature）；
2. 向 OAuth2 身份验证平台请求访问令牌（`AccessToken`）；
3. 处理身份验证平台返回的 JSON 响应。

如果响应中包含访问令牌，您就可以使用它向该服务账号有权限访问的 Unico 产品 API 发起请求。（如果响应中不包含访问令牌，则可能是您的 JWT 和令牌请求有误，或者该服务账号没有访问所请求资源的必要权限。）

上述请求生成的访问令牌默认有效期为 3600 秒，但这可能因贵公司设置的安全配置而有所不同。当访问令牌过期时，您的应用程序应生成一个新的 JWT，对其签名，并向身份验证平台请求一个新的访问令牌。

### 1 — 创建 JWT

JWT 由三部分组成：**头部**、**负载**和**签名**。头部和负载均为 JSON 对象。这些 JSON 对象先以 UTF-8 序列化，然后使用 **Base64url** 编码¹。这种编码方式在重复编码操作的情况下能提供更强的抗变化能力。头部、负载和签名之间用句点（`.`）字符连接。

JWT 的组成方式如下：

```
{Base64url 编码的头部}.{Base64url 编码的负载}.{Base64url 编码的签名}
```

用于签名的基础文本组成方式如下：

```
{Base64url 编码的头部}.{Base64url 编码的负载}
```

#### 1.1 — 构造 JWT 头部

头部由两个字段组成，用于指定签名算法和令牌格式。这两个字段均为必填项，且每个字段只有一个取值。服务账号依赖 RSA SHA-256 算法和 JWT 令牌格式。因此，头部的 JSON 表示如下：

```json
{"alg":"RS256","typ":"JWT"}
```

其 Base64url 表示如下：

```
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9
```

#### 1.2 — 构造 JWT 负载

JWT 负载包含关于该 JWT 的信息，包括请求的权限（作用域）、发起访问请求的账号、签发者、令牌签发时间以及令牌的有效期。大多数字段均为必填项。与 JWT 头部一样，负载也是一个 JSON 对象，并参与签名的构成。

#### 1.3 — 必填字段

JWT 中的必填字段如下表所示。它们在负载中可以以任意顺序出现。

| 名称 | 描述 |
|---|---|
| `iss` | 公司内该服务账号的标识符。 |
| `scope` | 应用程序所请求权限的列表，以空格或加号（`+`）分隔。如果需要该账号的全部权限，请使用星号（`*`）符号。 |
| `aud` | 签发访问令牌的身份验证平台地址。该值应始终精确为 `https://identityhomolog.acesso.io`。常见的**无效**写法包括：添加末尾斜杠（`https://identityhomolog.acesso.io/`），或使用 HTTP 而非 HTTPS。 |
| `exp` | 令牌的过期时间，以自 1970 年 1 月 1 日 00:00:00 UTC 起的秒数表示。该值的最长时长为 JWT 签发时间之后的 1 小时。它必须是**数值型**——像 `"1524161193"` 这样加引号的值属于字符串，将无法生效；`1524161193` 属于数字，可以生效。 |
| `iat` | JWT 的签发时间，以自 1970 年 1 月 1 日 00:00:00 UTC 起的秒数表示。该值必须是**数值型**，规则与 `exp` 相同。 |

`iat` 字段必须以所需格式表示当前时间，`exp` 字段必须遵循以下计算方式：

```
exp = iat + 3600
```

JWT 负载中必填 JSON 字段的表示如下：

```json
{
  "iss": "service_account_name@tenant_id.iam.acesso.io",
  "aud": "https://identityhomolog.acesso.io",
  "scope": "*",
  "exp": 1626296976,
  "iat": 1626293376
}
```

#### 1.4 — 计算签名

**JSON Web 签名（JWS）**²规范是指导 JWT 签名计算的机制。用于签名计算的输入内容是以下内容的字节数组：

```
{Base64url 编码的头部}.{Base64url 编码的负载}
```

计算签名时必须使用 JWT 头部中指定的相同算法。OAuth2 身份验证平台唯一支持的签名算法是使用 SHA-256 的 RSA，在 JWT 头部的 `alg` 字段中表示为 `RS256`。

使用与该服务账号一同创建并关联的私钥（从邮件收到的请求中生成的 `.key.pem` 文件），采用 SHA256withRSA（也称为使用 SHA-256 哈希的 RSASSA-PKCS1-V1_5-SIGN）对输入内容的 UTF-8 表示进行签名。输出内容将是一个字节数组。

随后需要将该签名进行 Base64url 编码。头部、负载和签名应以句点字符连接。结果即为该 JWT。

也可以使用现有的库来创建 JWT。作为参考，您可以在 [jwt.io](https://jwt.io/) 网站上找到相关库的列表。

### 2 — 请求访问令牌

生成已签名的 JWT 后，应用程序即可使用它来请求访问令牌。访问令牌请求是一个 POST HTTPS 请求，其请求体应进行 URL 编码。URL 如下所示：

```
https://identityhomolog.acesso.io/oauth2/token
```

以下参数在 POST HTTPS 请求中为必填项：

| 名称 | 描述 |
|---|---|
| `grant_type` | 使用以下文本，如有必要请进行 URL 编码：`urn:ietf:params:oauth:grant-type:jwt-bearer` |
| `assertion` | 该 JWT，包括签名。 |

```
POST /oauth2/token HTTP/1.1
Host: identityhomolog.acesso.io
Content-Type: application/x-www-form-urlencoded

grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=<jwt>
```

### 3 — 处理身份验证平台的响应

如果 JWT 和访问令牌请求格式正确，且该服务账号具有必要的权限，身份验证平台将返回一个包含访问令牌的 JSON 对象：

```json
{
  "access_token": "<access_token>",
  "token_type": "Bearer",
  "expires_in": "3600"
}
```

JSON 对象的 `access_token` 字段中返回的访问令牌本身也是一个 JWT 令牌，应在 Unico 产品的 API 中使用。如果请求过程中发生错误，请在[身份验证错误](./additional-resources/authentication-errors)中查看错误类型。

### 4 — 访问令牌的有效期

访问令牌的有效期是可变的。其时长在与访问令牌一同返回的 `expires_in` 字段中指定。在其有效期内，应对所有产品 API 调用使用同一个访问令牌。

**在当前令牌的有效期即将结束之前，请勿请求新的访问令牌。** 我们建议预留 600 秒（10 分钟）的余量：

```
new Date((token.exp - 600) * 1000)
```

其中 `token.exp` 是该令牌的过期时间戳。

:::note
默认情况下，发送给公司的令牌有效期为 1 小时，但该时长可以更改。建议始终以 `expires_in` 为基准，并从中减去 600 秒后再请求新令牌。
:::

**示例：**

```
标准场景：
expires_in: 3600（1 小时）— 令牌于 14:42 生成
仅在 15:32 才请求新令牌，即 14:42 + (3600 - 600)
```

```
时长被修改的场景：
expires_in: 7200（2 小时）— 令牌于 14:42 生成
仅在 16:32 才请求新令牌，即 14:42 + (7200 - 600)
```

:::warning
**请勿使用固定时间来获取新令牌，因为收到的令牌有效期可能短于设定的时间，这可能导致使用服务时出现故障。**
:::

---

¹ 根据 RFC 4648 中关于 BaseN 编码的规定，Base64url 格式与 Base64 类似，区别在于省略了 `=` 字符，并将 `+` 和 `/` 字符分别替换为 `-` 和 `_`。
² JSON Web 签名：[https://tools.ietf.org/html/rfc7515](https://tools.ietf.org/html/rfc7515)。