发布的 API 如果使用应用认证方式(ApiAppKey 和 ApiAppSecret),客户端在调用 API 时,需要使用签名密钥对请求内容进行签名计算,并将签名同步传输给服务器端进行签名验证,您可以参考本文档在客户端实现签名计算过程。
概述
API 网关提供前端签名及验签功能,该功能可实现:
验证客户端请求的合法性,确认请求中携带授权后的 App Key 生成的签名。
防止请求数据在网络传输过程中被篡改。
API 的拥有者可以在 API 网关控制台的应用管理页面生成 App,每个 App 会携带一对签名密钥(ApiAppKey 和 ApiAppSecret),API 拥有者将 API 授权给指定的 App(App 可以是 API 拥有者颁发或者 API 调用者所有)后,API 调用者就可以用 App 的签名密钥来调用相关的 API 了。
客户端调用 API 时,需要使用已授权签名密钥对请求内容的关键数据进行加密签名计算,并且将 ApiAppKey 和加密后生成的字符串放在请求的 Header 传输给 API 网关,API 网关会读取请求中的 ApiAppKey 的头信息,并且根据 ApiAppKey 的值查询到对应的 ApiAppSecret 的值,使用 ApiAppSecret 对收到的请求中的关键数据进行签名计算,并且使用自己的生成的签名和客户端传上来的签名进行比对,来验证签名的正确性。只有签名验证通过的请求才会发送给后端服务,否则 API 网关会认为该请求为非法请求,直接返回错误应答。
签名生成和认证流程
前置条件
被调用的 API 的安全认证类型为“应用认证”。
API 的调用方需要在调用 API 之前获取到对应 API 给应用的授权。
客户端生成签名
1. 从原始请求中提取关键数据,得到一个用来签名的字符串。
2. 使用加密算法加 ApiAppSecret 对关键数据签名串进行加密处理,得到签名。
3. 将签名所相关的所有头加入到原始 HTTP 请求中,得到最终 HTTP 请求。
服务器端验证签名
1. 从接收到的请求中提取关键数据,得到一个用来签名的字符串。
2. 从接收到的请求中读取 App Key,通过 App Key 查询到对应的 App Secret。
3. 使用加密算法和 App Secret 对关键数据签名串进行加密处理,得到签名。
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 为 date 和 source 时,此位置的形式为 headers="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="secret_id", algorithm="hmac-sha1", headers="date source", signature="Base64(HMAC-SHA1(signing_str, secret_key))"
Authorization 内各参数说明如下:
|
| |
| |
| 加密算法,当前支持的是 hmac-sha1 和 hmac-sha256 |
| |
| 计算签名后得到的签名,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="source x-date", signature="xyxyxyxyxyxy"
content-length:8
p=test
签名排错方法
问题描述
API 网关签名校验失败时,会将服务端的签名串(StringToSign)放到 HTTP Response 的 Header 中返回到客户端,错误码为401。
解决方法
1. 检查本地计算的签名串(StringToSign)与服务端返回的签名串是否一致。
2. 检查用于签名计算的 ApiAppSecret 是否正确。
由于 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"
说明服务器的签名是:
source: apigw test
x-date: Thu, 11 Mar 2021 08:29:58 GMT
POST
application/json
application/x-www-form-urlencoded
/?p=test
本页内容是否解决了您的问题?