跳到主要内容

认证测试

1. 简介

本节将说明请求的构建方式,以便被我们的系统接受。首先,需要在请求头的 API-CLIENT-KEY 中放入 QI CTVM 团队提供的 Api Key。然后,需要使用集成合作方的私钥创建 AUTHORIZATION 请求头进行签名;

以下将通过 Python 示例,逐步说明 AUTHORIZATION 的创建过程。

2. 导入库

本 Python 示例使用 5 个库来完成认证过程。

from datetime import datetime
import json
from jose import jwt
from hashlib import md5
import requests

3. 插入私钥和集成密钥

Dados da criptografia
api_key = "\<API KEY FORNECIDA PELA QI\>"

client_private_key = '''-----BEGIN EC PRIVATE KEY-----
MIHbAgEBBEH7OuewosJfz4zKF+Gm0ogJxhb8G6LSMDVQQbFYz335mHCx9/Pr6Yk+
yYwsVozeXhlry3/vnUn1zCasU+4O+yseZ6AHBgUrgQQAI6GBiQOBhgAEAa46fN/2
8vI64shRhu9erMA6JLl3zHFX8gFHQrbb0g4IDfjXCKMCILiwdtL8QecstsgepTa7
yo1pTXOVNDbmLX2TAK38xb2Gv6OC+PA+5drF2wWajWbVLpR2R7mYEzr5HNIAJYHb
5C1jvM2ItK2R22HAbYfH25nsvGhkCGbrRNWQVF9g
-----END EC PRIVATE KEY-----'''

4. 定义变量

定义每个请求特有的方法、端点和内容变量(本例中,我们使用 "POST" 方法访问 "/authentication_test" 端点)

Dados da requisição
base_url = "https://assignor-api.qidtvm.com.br"
today_str = datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S")
method = "POST"
endpoint = "/authentication_test"
body = {"name": "QI Tech"}
注意

签名中的 uri 字段必须与请求中发送的路径完全一致。如果该端点接收 query string 参数,这些参数同样属于被签名的 URI —— 请参见带 query string 的请求

5. 构建基础签名字典

Dicionário base

dict_to_sign = {"timestamp": today_str, "method": method, "uri": endpoint}

5.1. 如有必要,添加内容

对于含有 body 的请求,需要添加该内容的字节 md5。由于我们系统中的所有请求均通过 JSON 传输,可以使用以下方式:

Dicionário base
body_bytes = json.dumps(body).encode()

md5_instance = md5()
md5_instance.update(body_bytes)
md5_body = md5_instance.hexdigest()

dict_to_sign["payload_md5"] = md5_body

6. 对请求头进行加密

使用 JWT 库进行加密(本代码示例中,我们在 JavaScript 中使用 jsonwebtoken 作为 jwt)

jwt_headers = {"alg": "ES512", "typ": "JWT"}
encoded_header_token = jwt.encode(
claims=dict_to_sign,
key=client_private_key,
algorithm="ES512",
headers=jwt_headers,
)

7. 组装最终请求头

headers = {"API-CLIENT-KEY": api_key, "AUTHORIZATION": encoded_header_token}
Definindo url final
url = f"{base_url}{endpoint}"

发送请求

resp = requests.post(url=url, headers=headers, json=body)
print(resp.json())

带 query string 的请求

分页或带筛选条件的端点通过 query string 接收参数,例如 ?page=0&limit=20。在这类请求中,query string 属于被签名的 URI

uri 字段必须与请求中发送的路径一致:参数相同、顺序相同、编码相同。如果签名的 URI 与发送的 URI 不一致,请求将以状态码 401 被拒绝:

响应
{
"title": "Invalid endpoint",
"description": "Invalid endpoint.",
"translation": "O endpoint e invalido",
"code": "MIT000015"
}

示例

带分页的 GET 请求
base_url = "https://manager-api.qidtvm.com.br"
path = "/quota/fund_class/{fund_class_key}/investor_positions"
query_string = "page=0&limit=20"

# query string 属于被签名的 uri
uri = f"{path}?{query_string}"

method = "GET"
today_str = datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S")

dict_to_sign = {"timestamp": today_str, "method": method, "uri": uri}

jwt_headers = {"alg": "ES512", "typ": "JWT"}
encoded_header_token = jwt.encode(
claims=dict_to_sign,
key=client_private_key,
algorithm="ES512",
headers=jwt_headers,
)

headers = {"API-CLIENT-KEY": api_key, "AUTHORIZATION": encoded_header_token}

resp = requests.get(url=f"{base_url}{uri}", headers=headers)
print(resp.json())

建议

  • 请自行拼接 query string 并发送完整 URL。不要在已签名的 URI 之外再使用 requestsparams 参数:HTTP 客户端可能会重新排序或重新编码参数,从而使签名失效。
  • GET 请求通常没有请求体。此时请不要发送 body,也不要发送 payload_md5 字段。
  • 含有特殊字符的值(证件号中的标点、日期、空格)必须以最终将在 URL 中发送的形式进行签名。