有奖捉虫:办公协同&微信生态&物联网文档专题 HOT
发布的 API 如果使用应用认证方式,客户端在调用 API 时,需要使用签名密钥对请求内容进行签名计算,并将签名同步传输给服务器端进行签名验证,您可以参考本文档在客户端实现签名计算过程。
说明
常用语言的应用认证签名 Demo 请参见 开发指南-多种语言生成应用认证签名

概述

API 网关提供前端签名及验签功能,该功能可实现:
验证客户端请求的合法性,确认请求中携带授权后的(ApiAppKey 和 ApiAppSecret)生成的签名密钥。
防止请求数据在网络传输过程中被篡改。
1. API 的拥有者可以在 API 网关控制台的应用管理页面新建一个应用App,每个 App 会自动携带一对签名密钥(即ApiAppKey 和 ApiAppSecret);
2. API 拥有者将 API 授权给指定的 App(App 可以是 API 拥有者颁发或者 API 调用者所有,是一个具体的客户端);
3. API 调用者就可以用 App 的签名密钥来调用相关的 API 了。
4. 客户端调用 API 时,需要使用已授权签名密钥(即ApiAppKey 和 ApiAppSecret)对请求内容的关键数据进行加密签名,并且将 ApiAppKey 和加密后生成的字符串放在请求的 Header 传输给 API 网关;下文可查看详细签名计算方法。
5. API 网关会读取请求Header中的 ApiAppKey ,依据它查询对应的 ApiAppSecret值 ,使用 ApiAppSecret 对请求中的关键数据进行签名计算(加密算法来自用户设定的算法,详见下文)。然后使用这份自行生成的签名和客户端传上来的签名进行二者比对,从而验证签名的正确性。只有签名验证通过的请求才会发送给后端服务,否则 API 网关会认为该请求为非法请求,直接返回错误码应答。
注意:
调用凭证-应用列表中,新建应用后将自带一对密钥,由系统生成、不可自定义,是隶属于这个应用 App 的密钥,在 API 的鉴权类型为应用认证使用。
调用凭证-密钥列表中,既可自定义、也可由系统帮助生成,在API的鉴权类型为密钥对时使用。
这是两种完全不同的鉴权认证类型。API 的鉴权类型,对应如下位置:




签名生成和认证流程

前置条件

被调用的 API 的鉴权类型为“应用认证”。
API 的调用方需要在调用 API 之前获取到对应 API 给应用的授权,也即API拥有者提供了一对签名密钥(ApiAppKey 和 ApiAppSecret)。

客户端生成签名

1. 从原始请求中提取关键数据,得到一个初始字符串。
2. 使用加密算法和 ApiAppSecret 对初始字符串进行加密处理,得到最终签名。
3. 将签名所相关的所有字段加入到原始 HTTP 请求header中,得到最终 HTTP 请求。

服务器端验证签名

1. 从接收到的请求中提取关键数据,得到一个用来签名的字符串。
2. 从接收到的请求中读取ApiAppKey,通过它查询到服务器端对应的ApiAppSecret。
3. 使用加密算法和ApiAppSecret对关键数据签名串进行加密处理,得到服务器端签名。
4. 从接收到的请求中读取客户端签名,对比服务器端签名、客户端签名的一致性。

生成与传递签名

提取签名串

客户端需要从 HTTP 请求中提取出关键数据,组合成一个签名串。生成的签名串的格式如下:
Headers
HTTPMethod
Accept
Content-Type
Content-MD5
PathAndParameters
以上6个字段构成整个签名串,字段之间使用 \\n 间隔,Headers 必须包含 X-Date,PathAndParameters 后不需要加 \\n,其他字段如果为空都需要保留 \\n。签名大小写敏感。每个字段的提取规则如下:
Headers:用户可以选取指定的 Header 参与签名。 参与签名计算的 Header 的 Key 按照字典排序后使用如下方式拼接:
HeaderKey1 + ": " + HeaderValue1 + "\\n"\\+
HeaderKey2 + ": " + HeaderValue2 + "\\n"\\+
...
HeaderKeyN + ": " + HeaderValueN + "\\n"
Authorization 中 headers 位置填入的需要是参与计算签名的 header 的名称,并建议转换为小写,以 ascii 空格分隔。
例如,参与计算的 header 为 x-date 和 source 时,此位置的形式为 headers="x-date source";参与计算的 header 仅为 x-date 时,此位置的形式为 headers="x-date"。
HTTPMethod:HTTP 的方法,全部大写(如 POST)。
Accept:请求中的 Accept 头的值,可为空。建议显示设置 Accept Header。当 Accept 为空时,部分 HTTP 客户端会给 Accept 设置默认值为 */*,导致签名校验失败。
Content-Type:请求中的 Content-Type 头的值,可为空。
Content-MD5:请求中的 Content-MD5 头的值,可为空只有在请求存在 Body 且 Body 为非 Form 形式时才计算Content-MD5 头。Java 的 Content-MD5 值的参考计算方式如下:
String content-MD5 = Base64.encodeBase64(MD5(bodyStream.getbytes("UTF-8")));
PathAndParameters:包含 Path、Query 和 Form 中的所有参数,具体组织形式如下:
path不包含 发布环境(release、prepub、test)信息。
Query 和 Form 参数对的 Key 按照字典排序后使用上面的方式拼接。
Query 和 Form 参数为空时,则直接使用 Path,不需要添加 。
参数的 Value 为空时只保留 Key 参与签名,等号不需要再加入签名。
Query 和 Form 存在数组参数时(key 相同,value 不同的参数) ,Value 按照字典排序后使用上面的方式拼接。

签名示例

以一个普通的 HTTP 请求为例:
POST / HTTP/1.1
host:service-3rmwxxxx-1255968888.cq.apigw.tencentcs.com
accept:application/json
content-type:application/x-www-form-urlencoded
source:apigw test
x-date:Thu, 11 Mar 2021 08:29:58 GMT
content-length:8
p=test
生成的正确签名串为:
source: apigw test
x-date: Thu, 11 Mar 2021 08:29:58 GMT
POST
application/json
application/x-www-form-urlencoded


/?p=test

计算签名

客户端从 HTTP 请求中提取出关键数据组装成签名串后,需要对签名串进行加密及编码处理,形成最终的签名。步骤如下:
1. 将签名串(signing_str 表示签名前的初始内容)使用 UTF-8 解码后得到 Byte 数组。
2. 使用加密算法对 Byte 数组进行加密。
3. 使用 Base64 算法进行编码,形成最终的签名。

传输签名

客户端需要将 Authorization 放在 HTTP 请求中传输给 API 网关,进行签名校验。 Authorization header 格式如下:
Authorization: hmac id="ApiAppKey", algorithm="hmac-sha1", headers="x-date source", signature=Base64(HMAC-SHA1(signing_str, id)),
Authorization 内各参数说明如下:
参数
说明
hmac
固定内容,用于标识计算方法
id
其值为密钥内的 ApiAppKey的值
algorithm
加密算法,当前支持2种, hmac-sha1 和 hmac-sha256
headers
参与签名计算的 header,此处假设需参与计算的 header 为 x-date 和 source
signature
签名加密后得到的最终签名,signing_str 是签名前的内容
携带签名的完整 HTTP 请求的示例如下:
POST / HTTP/1.1
host:service-3rmwxxxx-1255968888.cq.apigw.tencentcs.com
accept:application/json
content-type:application/x-www-form-urlencoded
source:apigw test
x-date:Thu, 11 Mar 2021 08:29:58 GMT
Authorization:hmac id="xxxxxxx", algorithm="hmac-sha1", headers="x-date source", signature="xyxyxyxyxyxy"
content-length:8
p=test

签名排错方法

问题描述: API 网关签名校验失败时,会将服务端的签名串(StringToSign)放到 HTTP Response 的 Header 中返回到客户端,错误码为401。
解决方法:
1. 检查本地计算的签名串(StringToSign)与服务端返回的签名串是否一致。
2. 检查用于签名计算的 ApiAppSecret 是否正确。 说明服务器的签名是:
source: apigw test
x-date: Thu, 11 Mar 2021 08:29:58 GMT
POST
application/json
application/x-www-form-urlencoded

/?p=test
由于 HTTP Header 中无法表示换行,因此 StringToSign 中的换行符都被替换成 #
"message":"HMAC signature does not match, Server StringToSign:source: apigw test#x-date: Thu, 11 Mar 2021 08:49:30 GMT#POST#application\\/json#application\\/x-www-form-urlencoded##\\/?p=test"

多种语言应用认证的签名示例

上述是生成、传递、计算签名的流程,下列给出不同开发语言的应用认证签名示例。