讯飞语音-接口调用流程

在调用讯飞开放平台提供的语音服务接口前,必须先完成鉴权。鉴权过程依赖接口密钥(API Key、API Secret)进行多次加密与签名计算,通常包括时间戳拼接、参数排序、HMAC-SHA256 计算以及 Base64 编码等步骤。客户端需要将生成的签名信息附加在请求参数中,由服务端进行校验,以确保请求来源的合法性与完整性。只有当鉴权校验通过后,接口调用才能继续执行。

前言

讯飞各类语音服务的接口,调用流程基本一致。 本文以语音听写服务为例进行说明,帮助理解整体调用逻辑。

具体的接口调用流程,接口调用流程 提供了详细说明,可直接查阅,以下步骤仅作参考。

这里有一个Demo,可以用来测试:鉴权url生成示例记得替换自己的APPIDAPI_KEYAPI_SECRET

接口鉴权

  1. 在握手阶段,请求方需要对请求进行签名,服务端通过签名来校验请求的合法性。
  2. 鉴权方法:在请求地址后面加上鉴权相关参数。(参考以下鉴权URL示例)
wss://iat-api.xfyun.cn/v2/iat?authorization=YXBpX2tleT0ia2V5eHh4eHh4eHg4ZWUyNzkzNDg1MTlleHh4eHh4eHgiLCBhbGdvcml0aG09ImhtYWMtc2hhMjU2IiwgaGVhZGVycz0iaG9zdCBkYXRlIHJlcXVlc3QtbGluZSIsIHNpZ25hdHVyZT0iSHAzVHk0WmtTQm1MOGpLeU9McFFpdjlTcjVudm1lWUVIN1dzTC9aTzJKZz0i&date=Wed%2C%2010%20Jul%202019%2007%3A35%3A43%20GMT&host=iat-api.xfyun.cn
  1. 鉴权参数

image-20251016140244510

host

不同语音服务对应的服务器地址不同,你可以在指定服务的开发文档中找到,比如:语音听写服务的地址参考如下:

  1. 中英文(推荐使用):iat-api.xfyun.cn
  2. 中英文: ws-api.xfyun.cn
  3. 小语种:iat-niche-api.xfyun.cn

参考:接口要求

date

  1. date必须是UTC+0GMT时区,RFC1123格式(Wed, 10 Jul 2019 07:35:43 GMT)。
  2. 服务端会对Date进行时钟偏移检查,最大允许300秒的偏差,超出偏差的请求都将被拒绝。

authorization

authorization_origin

参数authorizationbase64编码前(authorization_origin)的格式如下

api_key="$api_key", algorithm="hmac-sha256", headers="host date request-line", signature="$signature"
  • api_key 是在 控制台 获取的APIKey
  • algorithm 是加密算法(仅支持hmac-sha256)。
  • headers 是参与签名的参数,是固定的参数名(“host date request-line”),而非参数的值。
  • signature 需要通过加密得到,是使用加密算法对参与签名的参数签名后并使用base64编码的字符串。

获取密钥

  1. 获取接口密钥APIKeyAPISecret
  2. 讯飞开放平台-创建新应用 创建WebAPI平台应用并添加语音听写(流式版)服务后即可查看。
image-20251016141459020

signature_origin

signature 原始字段由 hostdaterequest-line 三个参数按照格式拼接而成,拼接格式如下(其中 \n 为换行符,: 后面有一个空格):

host: $host\ndate: $date\n$request-line
示例

假设 url = wss://iat-api.xfyun.cn/v2/iatdate = Wed, 10 Jul 2019 07:35:43 GMT,那么signature原始字段(signature_origin)则为

host: iat-api.xfyun.cn
date: Wed, 10 Jul 2019 07:35:43 GMT
GET /v2/iat HTTP/1.1

对signature_origin签名

使用 hmac-sha256 算法结合 apiSecretsignature_origin 签名,获得签名后的摘要signature_sha。其中apiSecret是在控制台获取的APISecret

signature_sha=hmac-sha256(signature_origin, $apiSecret)

关于 hmac-sha256hmac-sha256

对signature_sha编码

使用base64编码对signature_sha进行编码,获得最终的signature

signature=base64(signature_sha)

关于 base64base64

拼接authorization

根据以上信息拼接 authorization base64编码前(authorization_origin)的字符串。

api_key="keyxxxxxxxx8ee279348519exxxxxxxx", algorithm="hmac-sha256", headers="host date request-line", signature="Hp3Ty4ZkSBmL8jKyOLpQiv9Sr5nvmeYEH7WsL/ZO2Jg="
注意

headers是参与签名的参数,请注意是固定的参数名(“host date request-line”),而非这些参数的值。

对authorization_origin编码

authorization_origin 进行 base64 编码获得最终的 authorization 参数。

authorization = base64(authorization_origin)

关于 base64base64

鉴权url生成示例

鉴权url生成示例

鉴权结果

  1. 如果握手成功,会返回HTTP 101状态码,表示协议升级成功。
  2. 如果握手失败,则根据不同错误类型返回不同HTTP Code状态码,同时携带错误描述信息,详细错误说明如下:

image-20251016141601353

握手失败返回示例
HTTP/1.1 401 Forbidden
Date: Thu, 06 Dec 2018 07:55:16 GMT
Content-Length: 116
Content-Type: text/plain; charset=utf-8
{
    "message": "HMAC signature does not match"
}

接口数据传输与接收

  1. 握手成功后客户端和服务端会建立Websocket连接,客户端通过Websocket连接可以同时上传和接收数据。当服务端有识别结果时,会通过Websocket连接推送识别结果到客户端。
  2. 发送数据时,如果间隔时间太短,可能会导致引擎识别有误。
  3. 建议每次发送音频间隔40ms,每次发送音频字节数(即java示例demo中的frameSize)为一帧音频大小的整数倍。
  4. 整个会话时长最多持续60s,或者超过10s未发送数据,服务端会主动断开连接。
  5. 数据上传完毕,客户端需要上传一次数据结束标识表示会话已结束。

更多说明参考官网:接口调用流程

请求参数

image-20251016141728370

公共参数

image-20251016141818215

业务参数

image-20251016141839710

业务数据流参数

image-20251016141904470

请求参数示例

{
 "common": {
     // 公共请求参数
     "app_id": "123456"
 },
 "business": {
     "language": "zh_cn",
     "domain": "iat",
     "accent": "mandarin"
 },
 "data": {
     "status": 0,
     "format": "audio/L16;rate=16000",
     "encoding": "raw",
     "audio":"exSI6ICJlbiIsCgkgICAgInBvc2l0aW9uIjogImZhbHNlIgoJf..."
 }
}
数据上传结束标识示例
{
    "data":{
    	"status":2
    }
}

返回参数

image-20250919154640684

动态修正返回参数

若开通了动态修正功能并设置了dwa=wpgs(仅中文支持),还有如下字段返回:

image-20250919154705840

vinfo返回参数

若设置了vinfo=1,还有如下字段返回(若同时开通并设置了dwa=wpgs,则vinfo失效):

image-20250919154723638

返回参数示例

{
	"code": 0,
	"message": "success",
"sid": "iatxxxxxxxxxxxxx",
"data": {
 "result": {
   "bg": 0,
   "ed": 0,
   "ls": false,
   "pgs": "rpl",
   "rg": [
     1,
     1
   ],
   "sn": 2,
   "ws": [
     {
       "bg": 0,
       "cw": [
         {
           "sc": 0,
           "w": "测试"
         }
       ]
     },
     {
       "bg": 0,
       "cw": [
         {
           "sc": 0,
           "w": "一下"
         }
       ]
     }
   ]
 },
 "status": 1
}
}

错误码

官网:错误码

加密算法说明

hmac-sha256

  1. HMAC-SHA256 是一种基于散列函数的消息认证码(HMAC),常用于:API 请求签名、消息完整性验证、用户身份认证。
  2. 它结合了 SHA-256 哈希算法 和 一个密钥(key),生成一个固定长度的摘要(MAC),确保数据未被篡改且来源可信。

HMAC-SHA256 工作原理

HMAC(key, message) = SHA256((keyopad) || SHA256((keyipad) || message))
  • 是异或操作
  • opad / ipad 是固定填充值
  • || 表示拼接

示例

Node.js
const crypto = require('crypto');
 
const message = 'hello';
const key = 'my-secret-key';
 
const hmac = crypto.createHmac('sha256',key).update(message).digest('base64');
console.log(hmac); 
Java
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
 
public class HmacSha256Java17 {
 
    public static String hmacSha256(String key, String message) throws Exception {
        Mac mac = Mac.getInstance("HmacSHA256");
        SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
        mac.init(secretKey);
        
        byte[] hash = mac.doFinal(message.getBytes(StandardCharsets.UTF_8));
        
        // 返回 Base64 编码结果(也可以改为十六进制编码)
        return Base64.getEncoder().encodeToString(hash);
      }
  
    public static void main(String[] args) throws Exception {
        String key = "my-secret-key";
        String message = "hello";
        
        String signature = hmacSha256(key, message);
        System.out.println(signature);
     }
}

使用第三方库,更加简洁

  1. 添加 Maven 依赖
<dependency>
  <groupId>commons-codec</groupId>
  <artifactId>commons-codec</artifactId>
  <version>1.16.0</version>
</dependency>
  1. 加密
import org.apache.commons.codec.digest.HmacUtils;
 
String sign = HmacUtils.hmacSha256Hex("secret", "message");

base64

  1. Base64 是一种将二进制数据(如文件、字节流等)转换为 ASCII 字符串格式的编码方式,广泛应用于数据传输,如在 URL 中传输二进制数据、API 请求中的签名等。
  2. 它的主要优点是兼容性好,避免了直接传输二进制可能导致的乱码或协议冲突问题。
  3. 缺点是数据膨胀,Base64 编码后的数据大约会比原始二进制数据增加 33%。

Base64 编码/解码原理

  1. 编码:将 3 字节(24 bits)转换为 4 个字符,每个字符是从 Base64 字符集(包含 A-Z、a-z、0-9、+、/)中选择的。
  2. 解码:将 4 个 Base64 字符恢复为 3 字节。

示例

JavaScript
  1. 编码字符串为 Base64
const str = "Hello, World!";
const encoded = btoa(str);  // base64 编码,btoa() = binary to ASCII
console.log(encoded);       // 输出: "SGVsbG8sIFdvcmxkIQ=="
  1. 解码 Base64 为字符串
const str = "Hello, World!";
const encoded = btoa(str); 
const decoded = atob(encoded); // base64 解码,atob() = ASCII to binary
console.log(decoded);          // 输出: "Hello, World!"

⚠️ 注意:btoa() 只能处理 ASCII 字符串(即 Latin-1),如果你的字符串包含中文或 emoji,推荐使用下面的安全方式

function encodeBase64(str) {
  return btoa(unescape(encodeURIComponent(str)));
}
 
function decodeBase64(str) {
  return decodeURIComponent(escape(atob(str)));
}
 
const base64 = encodeBase64("你好,世界");
console.log(base64);                    // 输出:"5L2g5aW977yM5LiW55WM"
console.log(decodeBase64(base64));     // 输出:你好,世界
Node.js
const str = "Hello, Node!";
const base64 = Buffer.from(str).toString('base64');
console.log(base64); // 输出:"SGVsbG8sIE5vZGUh"
 
const decoded = Buffer.from(base64, 'base64').toString();
console.log(decoded); // 输出:Hello, Node!
Java
import java.util.Base64;
 
public class Base64Example {
 public static void main(String[] args) {
     String message = "Hello, World!";
     
     // 编码
     String encoded = Base64.getEncoder().encodeToString(message.getBytes());
     System.out.println("Encoded: " + encoded); // 输出:SGVsbG8sIFdvcmxkIQ==
 
     // 解码
     byte[] decodedBytes = Base64.getDecoder().decode(encoded);
     String decoded = new String(decodedBytes);
     System.out.println("Decoded: " + decoded); // 输出:Hello, World!
  }
}
 

Java 还支持 URL-safeBase64 编码,适用于在 URL 中传输 Base64 字符串时(避免使用 + 和 / )

import java.util.Base64;
 
public class Base64Example {
 public static void main(String[] args) {
     String message = "Hello, World!";
 
     // URL-safe 编码
     String encoded = Base64.getUrlEncoder().encodeToString(message.getBytes());
     System.out.println("URL-safe Encoded: " + encoded);
 
     // encoded 进行解码
     byte[] decodedBytes = Base64.getUrlDecoder().decode(encoded);
     String decoded = new String(decodedBytes);
     System.out.println("URL-safe Decoded: " + decoded); // 输出:Hello, World!
  }
}
 

相关文章

评论区