签名错误是开发者在接入API过程中非常常见的错误,如果使用的是PHP或者Java,建议基于官网提供的demo代码来改造,基本能避免这个问题。常见的签名错误分为代码实现错误、调用方式错误和其他错误这几类,以下展开来讲解,并介绍验证签名的简易方法。
签名代码常见错误:
官网提供了多种编程语言的签名实现,基本能满足大部分后台开发的需求。对于其他语言的实现以Go语言为例讲解一下需要注意的点,签名代码如下:
代码语言:go复制/**
* 生成签名
*
* @param secretId 邮件下发的secret_id
* @param secretKey 邮件下发的secret_key
* @param httpMethod http请求方法 GET/POST/PUT等
* @param headerNonce X-TC-Nonce请求头,随机数
* @param headerTimestamp X-TC-Timestamp请求头,当前时间的秒级时间戳
* @param requestUri 请求uri,eg:/v1/meetings
* @param requestBody 请求体,没有的设为空串
* @return 签名,需要设置在请求头X-TC-Signature中
*/
func DoSignature(secretId string, secretKey string, httpMethod string, headerNonce string, headerTimestamp string, requestUri string, requestBody string) string {
//1、串联Header参数
headSignStr := fmt.Sprintf("X-TC-Key=%s&X-TC-Nonce=%s&X-TC-Timestamp=%s",
secretId, headerNonce, headerTimestamp)
//2、组签名串
signStr := fmt.Sprintf("%sn%sn%sn%s", httpMethod, headSignStr, requestUri, requestBody)
//3、计算签名
h := hmac.New(sha256.New, []byte(secretKey))
h.Write([]byte(signStr))
sha := hex.EncodeToString(h.Sum([]byte{}))
//4、Base64编码
signBase64 := base64.StdEncoding.EncodeToString([]byte(sha))
return signBase64
}
根据官网的介绍,我们可以知道签名是分4步的:1、串联Header参数;2、组签名串;3、SHA256计算签名;4、Base64编码。
常见的代码错误主要有3个:
- 串联Header参数时没有按照字典升序排列。建议直接按示例代码的参数顺序来串联Header参数。
- 调试过程中,对GET方法签名时,由于消息体为空,第2步组签名串时少写了一个换行符(一共有3个),导致计算签名不对。因此不管requestBody是否为空我们都要传入这个参数,为空时传空串。
- 开发者忘记做第4步的Base64转码。
使用方式常见错误:
- 签名时secretId和secretKey这两个参数写反。
- 以创建会议接口为例,传递的requestUri参数为https://api.meeting.qq.com/v1/meetings,实际requestUri是不包含前面的域名的,传/v1/meetings就可以了。
- http头参数里面X-TC-Key为secretId,错写成secretKey。
- http头参数里面没有带SdkId。
- 签名时传的消息体requestBody与实际传输的不一致,常见于手工填写消息体json串导致两边不一样,建议直接用代码将结构体转换成json串。
其他错误:
- 请求内容全部为英文时调用OK,为中文时调用签名报错。这种情况需要将编码格式修改为utf-8 。
- 上面这种错误修改为utf-8之后仍然报错,这种情况一般是http插件的原因。具体原因是插件在传输时对中文做了Unicode编码,但是在计算签名时没有做,导致签名报错。这种情况优先升级http插件解决,如果特殊原因无法修改http插件,可以在计算签名时,将传入的消息体参数中的中文做Unicode转换,然后用转换后的字符串参与签名计算,这样签名计算使用的消息体编码方式就与实际传输的保持一致了。具体转换的代码(Java)如下:
public static String chineseToUnicode(String str) {
String result = "";
for (int i = 0; i < str.length(); i ) {
int chr1 = (char)str.charAt(i);
// 汉字范围 u4e00 - u9fa5
if (chr1 >= 19968 && chr1 <= 171941) {
result = "\u" Integer.toHexString(chr1);
} else {
result = str.charAt(i);
}
}
return result;
}
签名验证方法:
在完成接口开发之前是无法通过向服务器发送请求来确认签名是否正确的,因此需要借助官网提供的开发者工具来验证我们的签名算法。为了简单起见,验证签名算法时我们使用没有消息体参数的GET方法,以通过会议ID查询这个接口为例讲讲怎么使用。
1. 填写账号信息
2. 填写请求参数
3. 生成签名
按下图填好参数后依次点击生成当前时间戳,生成一个随机数和获取签名按钮。
4. 获取签名参数及结果用于代码验证
点击发送请求,然后在下面的原始请求处获取用于签名的参数及签名结果,然后将同样的参数放入自己的签名代码中验证。注意不要在上面的请求头界面直接双击拷贝签名结果,会漏掉最后的两个“=”导致签名计算结果不一样。