有奖捉虫:办公协同&微信生态&物联网文档专题 HOT
创建应用后即可获取应用凭证信息,具体可参见 企业自建应用接入指引

公共参数

公共参数是用于标识用户和接口鉴权目的的参数,如非必要,在每个接口单独的接口文档中不再对这些参数进行说明,但每次请求均需要携带这些参数,才能正常发起请求。 API 采用 TC3-HMAC-SHA256 签名方法,公共参数需要统一放到 HTTP Header 请求头部中。
参数名称
参数类型
必选
参数描述
Content-Type
String
内容类型,传入格式必须为 application/json。
X-TC-Action
String
操作的接口名称。注意:某些接口不需要传递该参数,接口文档中会对此特别说明,此时即使传递该参数也不会生效。
X-TC-Region
String
地域参数,用来标识希望操作哪个地域的数据。注意:某些接口不需要传递该参数,接口文档中会对此特别说明,此时即使传递该参数也不会生效
X-TC-Key
String
此参数参与签名计算。此参数为应用安全凭证密钥对中的 SecretId。企业管理员可以登录 腾讯会议官网,单击右上角用户中心,点击左侧菜单栏中的企业管理 > 高级 > REST API 进入到企业自建应用的[基础信息和凭证]中查看。
X-TC-Timestamp
String
此参数参与签名计算。当前 UNIX 时间戳,可记录发起 API 请求的时间。例如1529223702,单位为秒。注意:如果与服务器时间相差超过5分钟,会引起签名过期错误。
X-TC-Nonce
String
此参数参与签名计算。随机正整数。
X-TC-Signature
String
放置由下面的签名方法产生的签名。
AppId
String
腾讯会议分配给企业的企业 ID。企业管理员可以登录 腾讯会议官网,单击右上角用户中心,在左侧菜单栏中的企业管理 > 账户管理 > 账户信息中进行查看。开发者可以单击右上角用户中心,在左侧菜单栏中的企业管理 > 高级 > REST API 应用信息中查看。
SdkId
String
用户子账号或开发的应用 ID,企业管理员可以登录 腾讯会议官网,单击右上角用户中心,在左侧菜单栏中的企业管理 > 高级 > REST API 中进行查看(如存在 SdkId 则必须填写,早期申请 API 且未分配 SdkId 的客户可不填写)。
X-TC-Registered
String
启用账户通讯录,传入值必须为1,创建的会议可出现在用户的会议列表中。
启用账户通讯录说明:
1. 通过 SSO 接入腾讯会议账号体系。
2. 通过调用接口创建企业用户。
3. 通过企业管理后台添加或批量导入企业用户。
注意:
SecretId 和 SecretKey 是您调用 API 的重要凭证,请妥善保管,切勿泄漏。
构造请求头,需注意自定义字段名的大小写。签名验证以及服务器端读取字段值对大小写敏感。

生成签名串

参与签名的字符串包括:
注意:
此处为伪代码,拷贝粘贴不保证可编译运行。
String stringToSign=
HTTPMethod + "\\n" + //POST, GET
Headers + "\\n" + //指定的Header参数, X-TC-Nonce, X-TC-Timestamp, X-TC-Key
URI + "\\n" + //eg: https://api.meeting.qq.com/v1/meetings, URI=/v1/meetings
Params //Body中JSON序列化后的参数,GET方法没有请求体,Body需要带空串""参与计算

Header 中参与签名的字段包含:X-TC-Nonce,X-TC-Timestamp,X-TC-Key。 组成 Header 签名串时,参与签名的参数按参数名做字典序升序排列。X-TC-Signature 为计算后的签名字段,不参与签名计算。标准的 HTTP Header 非空参数本手册约定不参与签名计算。
如果是带有查询参数的请求,URI 含所有的查询串,例如查询会议请求:
注意:
此处为伪代码,拷贝粘贴不保证可编译运行。
GET https://api.meeting.qq.com/v1/meetings/7567173273889276131?userid=tester1&instanceid=1

URI为"/v1/meetings/7567173273889276131?userid=tester1&instanceid=1"
例如,取消会议的 HTTP POST 请求示例:
注意:
此处为伪代码,拷贝粘贴不保证可编译运行。
POST http://api.meeting.qq.com/v1/meetings/7567454748865986567/cancel
X-TC-Key: AKID********************EXAMPLE
X-TC-Timestamp:1572168600
X-TC-Nonce:88080
content-type:application/json
AppId:1234567890

{
"userid" : "test1",
"instanceid" : 1,
  "reason_code" : 1,
  "reason_detail" : "取消会议"
}
步骤1:串联 Header 参数。
注意:
此处为伪代码,拷贝粘贴不保证可编译运行。
headerString = "X-TC-Key=" + "AKIDz8krbsJ*********************XAMPLE" + "&X-TC-Nonce=" + 1234567 + "&X-TC-Timestamp=" + 1572168600
步骤2:组签名串。
注意:
此处为伪代码,拷贝粘贴不保证可编译运行。
stringToSign = POST + "\\n" +
headerString + "\\n" +
"/v1/meetings/7567454748865986567/cancel" + "\\n" +
"{"userid":"test1","instanceid":1,"reason_code":1,"reason_detail":"取消会议"}"

计算签名

拼接好待签名的字符串后,用 SecretKey 密钥生成待签名字符串的 Hmac-SHA256 签名,将签名转换为16进制字符串形式,然后进行 Base64 编码。
参考以下 Java 示例代码。

传递签名

对每一个 HTTP 请求,都需要将签名放到 Request 的 Header 参数 X-TC-Signature 中去。

代码示例

Java(Java8)
PHP
GO
Python
C++
C#
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

public interface SignatureUtil {


String HMAC_ALGORITHM = "HmacSHA256";

char[] HEX_CHAR = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};

static String bytesToHex(byte[] bytes) {

char[] buf = new char[bytes.length * 2];
int index = 0;
for (byte b : bytes) {
buf[index++] = HEX_CHAR[b >>> 4 & 0xf];
buf[index++] = HEX_CHAR[b & 0xf];
}

return new String(buf);
}

/**
* 生成签名,开发版本oracle jdk 1.8.0_221
*
* @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中
* @throws NoSuchAlgorithmException e
* @throws InvalidKeyException e
*/
static String sign(String secretId, String secretKey, String httpMethod, String headerNonce, String headerTimestamp, String requestUri, String requestBody)
throws NoSuchAlgorithmException, InvalidKeyException {

String tobeSig =
httpMethod + "\\nX-TC-Key=" + secretId + "&X-TC-Nonce=" + headerNonce + "&X-TC-Timestamp=" + headerTimestamp + "\\n" + requestUri + "\\n" + requestBody;
Mac mac = Mac.getInstance(HMAC_ALGORITHM);
SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), mac.getAlgorithm());
mac.init(secretKeySpec);
byte[] hash = mac.doFinal(tobeSig.getBytes(StandardCharsets.UTF_8));
String hexHash = bytesToHex(hash);
return new String(Base64.getEncoder().encode(hexHash.getBytes(StandardCharsets.UTF_8)));
}
}


class SignatureUtil
{
    private static $secret_id = '';  // SecretId
    private static $secret_key = ''; // SecretKey

    public static function setSecretIdAndKey($secret_id, $secret_key)
    {
        self::$secret_id = $secret_id;
        self::$secret_key = $secret_key;
    }

    public static function getSecretId()
    {
        return self::$secret_id;
    }

    /**
     * @param $header = [
     * 'X-TC-Nonce' => '',  // X-TC-Nonce请求头,随机数
     * 'X-TC-Timestamp' => '', // X-TC-Timestamp请求头,当前时间的秒级时间戳
     * 'URI' => ''  //请求uri,eg:/v1/meetings,
     * ];
     * @param $body   请求体,没有的设为空串
     * @param $method  请求方式 字符串 "POST","GET"
     */
    public static function sign($header, $body, $method)
    {
        if (empty(self::$secret_id) || empty(self::$secret_key)) {
            return null;
        }
        $secret_id = self::$secret_id;
        $header_string = "X-TC-Key={$secret_id}&X-TC-Nonce={$header['X-TC-Nonce']}&X-TC-Timestamp={$header['X-TC-Timestamp']}";
        $str_to_sign = "{$method}\\n{$header_string}\\n{$header['URI']}\\n{$body}";
        $hash = hash_hmac('sha256', $str_to_sign, self::$secret_key);
        return base64_encode($hash);
    }
}

/**
* 生成签名
*
* @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 {
headSignStr := fmt.Sprintf("X-TC-Key=%s&X-TC-Nonce=%s&X-TC-Timestamp=%s",
secretId, headerNonce, headerTimestamp)
signStr := fmt.Sprintf("%s\\n%s\\n%s\\n%s", httpMethod, headSignStr, requestUri, requestBody)
h := hmac.New(sha256.New, []byte(secretKey))
h.Write([]byte(signStr))
sha := hex.EncodeToString(h.Sum([]byte{}))
signBase64 := base64.StdEncoding.EncodeToString([]byte(sha))
return signBase64
}

def sign(secretId, secretKey, httpMethod, headerNonce, headerTimestamp, requestUri, requestBody):
tobeSig = "{0}\\nX-TC-Key={1}&X-TC-Nonce={2}&X-TC-Timestamp={3}\\n{4}\\n{5}".format(
httpMethod, secretId, headerNonce, headerTimestamp, requestUri, requestBody)
signature = hmac.new(secretKey.encode(
'utf-8'), tobeSig.encode('utf-8'), digestmod='sha256').hexdigest()
return base64.b64encode(signature.encode('utf-8'))

#include <iostream>
#include <string>
#include <openssl/hmac.h>
#include <openssl/buffer.h>

using namespace std;

//hmac-sha256
int HmacEncode(const char * key, unsigned int key_length,
const char * input, unsigned int input_length,
unsigned char * &output, unsigned int &output_length) {
const EVP_MD * engine = EVP_sha256();
output = (unsigned char*)malloc(EVP_MAX_MD_SIZE);
HMAC_CTX *ctx = HMAC_CTX_new();
HMAC_Init_ex(ctx, key, key_length, engine, NULL);
HMAC_Update(ctx, (unsigned char*)input, input_length);
HMAC_Final(ctx, output, &output_length);
HMAC_CTX_free(ctx);
return 0;
}

//base64
char * Base64Encode(const char * input, int length)
{
BIO * bmem = NULL;
BIO * b64 = NULL;
BUF_MEM * bptr = NULL;
b64 = BIO_new(BIO_f_base64());
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
bmem = BIO_new(BIO_s_mem());
b64 = BIO_push(b64, bmem);
BIO_write(b64, input, length);
BIO_flush(b64);
BIO_get_mem_ptr(b64, &bptr);
char * buff = (char *)malloc(bptr->length + 1);
memcpy(buff, bptr->data, bptr->length);
buff[bptr->length] = 0;
BIO_free_all(b64);
return buff;
}

//unsigned char 转 十六进制字符串
void byte_to_hex( const unsigned char* source, char* dest, int sourceLen) {
short i;
unsigned char highByte, lowByte;
for (i = 0; i < sourceLen; i++) {
highByte = source[i] >> 4;
lowByte = source[i] & 0x0f;
highByte += 0x30;
if (highByte > 0x39)
dest[i * 2] = highByte + 0x07;
else
dest[i * 2] = highByte;
lowByte += 0x30;
if (lowByte > 0x39)
dest[i * 2 + 1] = lowByte + 0x07;
else
dest[i * 2 + 1] = lowByte;
}
return;
}

/**
* 生成签名
*
* @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中
*/
string sign(string secretId, string secretKey, string httpMethod, string headerNonce, string headerTimestamp, string requestUri, string requestBody)
{
string tobeSig = httpMethod + "\\nX-TC-Key=" + secretId + "&X-TC-Nonce=" + headerNonce + "&X-TC-Timestamp=" + headerTimestamp + "\\n" + requestUri + "\\n" + requestBody;
unsigned char* mac = NULL;
unsigned int mac_length = 0;
HmacEncode(secretKey.c_str(), secretKey.length(), tobeSig.c_str(), tobeSig.length(), mac, mac_length);
char* dest;
byte_to_hex(mac,dest,mac_length);
string str = dest;
transform(str.begin(),str.end(),str.begin(),::tolower);
return Base64Encode(str.c_str(), 64);
}


/**
* 生成签名
*
* @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中
*/
private static string sign(string secretId, string secretKey, string httpMethod, string headerNonce, string headerTimestamp, string requestUri, string requestBody)
{
String tobeSig =
httpMethod + "\\nX-TC-Key=" + secretId + "&X-TC-Nonce=" + headerNonce + "&X-TC-Timestamp=" + headerTimestamp + "\\n" + requestUri + "\\n" + requestBody;
var encoding = System.Text.Encoding.UTF8;
byte[] keyByte = encoding.GetBytes(secretKey);
byte[] messageBytes = encoding.GetBytes(tobeSig);
using (var hmacsha256 = new HMACSHA256(keyByte))
{
byte[] hashmessage = hmacsha256.ComputeHash(messageBytes);
var b16 = BitConverter.ToString(hashmessage).Replace("-", "").ToLower();
return Convert.ToBase64String(encoding.GetBytes(b16));
}
}

说明:
如果是 GET 方法的 API 调用,例如:用会议码查询会议详情,则无请求体,签名计算时需要带入空("")的请求体。

鉴权错误返回参数

鉴权错误返回统一为400。