实战解析:阿里云语音识别 Token 获取全流程

2026-01-21 48 浏览 0 评论

在对接阿里云语音识别(NLS)服务时,Token 是鉴权的核心凭证,所有语音交互请求都需要携带有效的 Token 才能正常调用。本文将从业务场景出发,逐段拆解阿里云语音识别 Token 的获取逻辑,并详解代码实现的核心要点,帮助开发者快速掌握 Token 获取的完整流程。

一、核心业务逻辑概述

阿里云语音识别 Token 的获取本质是通过 OpenAPI 调用 CreateToken 接口,完成签名认证后获取临时凭证,并通过缓存机制避免重复请求(Token 有有效期,默认 24 小时)。本文示例代码基于 Node.js + Egg.js 框架实现,核心流程可概括为:

  1. 从缓存中读取已存在的 Token,若存在则直接返回;
  2. 若缓存无 Token,则构造 API 请求参数并完成签名;
  3. 调用阿里云 Token 创建接口,获取 Token 后存入缓存;
  4. 返回 Token 及语音识别服务的基础配置(如服务地址、AppKey)。

二、代码逐段解析

2.1 缓存查询:优先使用已存在的 Token

async stToken() {
    // 从 memcache 中获取缓存的 Token
    let token = await this.ctx.service.memcache.get('aliyuncloud_nls_token');
    if (typeof token === 'undefined') {
        // 缓存无 Token 时,执行 Token 创建逻辑
        // ... 后续 Token 创建代码
    }
    // 缓存有 Token 时,直接返回结果
    this.ctx.body = {
      code: 0,
      success: true,
      data: {
        token,
        url: 'wss://nls-gateway.cn-shanghai.aliyuncs.com/ws/v1',
        appkey: 'yFiBOd43tLhrTjH6',
      },
      message: '',
    };
}

关键说明

  • 缓存 Key 命名为 aliyuncloud_nls_token ,便于后续维护和排查;
  • 优先查询缓存是为了减少 API 调用次数,避免阿里云接口的限流风险,同时提升接口响应速度;
  • 若缓存中 tokenundefined (未缓存或已过期),则进入 Token 创建流程。

2.2 构造 API 请求参数:满足阿里云签名规范

const params = {
    AccessKeyId: this.app.config.alioss.AccessKeyID, // 阿里云 AccessKey ID
    Action: 'CreateToken', // 固定动作:创建 Token
    Version: '2019-02-28', // API 版本,固定值
    Format: 'JSON', // 返回格式,支持 JSON/XML
    RegionId: 'cn-shanghai', // 地域 ID,语音识别默认上海
    Timestamp: new Date().toISOString(), // 当前时间戳(ISO 格式)
    SignatureMethod: 'HMAC-SHA1', // 签名算法,固定 HMAC-SHA1
    SignatureVersion: '1.0', // 签名版本,固定 1.0
    SignatureNonce: [
      this.ctx.helper.random(8),
      this.ctx.helper.random(4),
      this.ctx.helper.random(4),
      this.ctx.helper.random(4),
      this.ctx.helper.random(12),
    ].join('-'), // 随机字符串,避免请求重放
};

关键说明

  • AccessKeyId 从配置文件读取,避免硬编码(生产环境需做好配置加密);
  • SignatureNonce 是随机字符串,通过分段随机拼接(8-4-4-4-12 格式),确保每次请求的唯一性,防止重放攻击;
  • 所有参数均为阿里云 CreateToken 接口的必填项,参数名和取值需严格遵循阿里云 OpenAPI 规范。

2.3 参数规范化:按字母排序并 URL 编码

// 1) 按字母顺序规范化参数
const sortedKeys = Object.keys(params).sort();
const queryString = sortedKeys.map(key => `${percentEncode(key)}=${percentEncode(params[key])}`).join('&');

关键说明

  • 阿里云签名要求参数按 首字母升序排列 ,因此先通过 Object.keys(params).sort() 获取排序后的参数名数组;
  • percentEncode 是 URL 编码函数(需自行实现,遵循 RFC3986 规范),对参数名和参数值分别编码,避免特殊字符导致签名错误;
  • 最终拼接成 key1=value1&key2=value2 格式的查询字符串。

2.4 构造待签名字符串:签名的核心步骤

// 2) 构造待签名字符串
const stringToSign = `GET&${percentEncode('/')}&${percentEncode(queryString)}`;

关键说明

  • 阿里云 OpenAPI 签名规则为: HTTP 方法&%2F&编码后的查询字符串%2F/ 的 URL 编码结果);
  • 因调用 CreateToken 接口使用 GET 方法,故开头为 GET&
  • 需对 / 和查询字符串分别编码后拼接,形成最终的待签名字符串。

2.5 计算签名:完成身份认证

// 3) 计算签名
const signature = signStringToSign(stringToSign, this.app.config.alioss.AccessKeySecret);

关键说明

  • signStringToSign 是签名函数,需基于 HMAC-SHA1 算法实现:
  function signStringToSign(stringToSign, accessKeySecret) {
      const crypto = require('crypto');
      const key = accessKeySecret + '&'; // 阿里云签名密钥需拼接&
      const hmac = crypto.createHmac('sha1', key);
      return hmac.update(Buffer.from(stringToSign, 'utf8')).digest('base64');
  }
  • AccessKeySecret 是阿里云账号的密钥,需严格保密,切勿泄露或提交到代码仓库;
  • 签名结果需进行 Base64 编码,得到最终的 signature

2.6 拼接请求 URL 并调用接口:获取 Token

// 4) 拼接到完整 URL
const url = `https://nls-meta.cn-shanghai.aliyuncs.com/?Signature=${signature}&${queryString}`;

const res = await this.ctx.curl(url, { method: 'get', dataType: 'json' }).catch(err => {
  this.ctx.returnError(err.message);
});

关键说明

  • 请求域名 nls-meta.cn-shanghai.aliyuncs.com 是阿里云语音识别元数据服务的固定域名(上海地域);
  • URL 拼接规则:基础域名 + ?Signature=签名值&原查询字符串
  • 使用 Egg.js 内置的 ctx.curl 发起 GET 请求,指定 dataType: 'json' 自动解析返回结果;
  • 增加异常捕获,若请求失败则通过 ctx.returnError 返回错误信息。

2.7 处理返回结果:缓存 Token 并返回

res.data = res.data || {};
if ('Token' in res.data) {
  token = res.data.Token;
  // 将 Token 存入缓存,设置过期时间(提前 1 小时过期,避免使用时已失效)
  await this.ctx.service.memcache.set(
    'aliyuncloud_nls_token',
    res.data.Token,
    Number(res.data.Token.ExpireTime + '000') - new Date().getTime() - 3600,
  );
} else {
  this.ctx.returnError(res.data.ErrMsg || res.data.Message || '获取 token 出错了');
}

关键说明

  • 阿里云返回的 res.data.Token 包含 Id (Token 值)、 ExpireTime (过期时间,单位秒)等信息;
  • 缓存过期时间设置为 Token 过期时间 - 当前时间 - 3600 秒 ,提前 1 小时过期,避免因网络延迟等问题使用到过期 Token;
  • 若返回结果无 Token 字段,优先返回阿里云的错误信息( ErrMsg/Message ),便于问题排查。

2.8 返回最终结果:提供语音识别所需配置

this.ctx.body = {
  code: 0,
  success: true,
  data: {
    token,
    url: 'wss://nls-gateway.cn-shanghai.aliyuncs.com/ws/v1', // 语音识别 WebSocket 地址
    appkey: 'yFiBOd43tLhrTjH6', // 阿里云语音识别应用 AppKey
  },
  message: '',
};

关键说明

  • 返回格式包含 code (状态码)、 success (是否成功)、 data (核心数据)、 message (提示信息),符合通用接口规范;
  • url 是阿里云语音识别的 WebSocket 服务地址(上海地域), appkey 是创建语音识别应用时生成的应用密钥,需替换为自身的 AppKey。

三、完整代码(含辅助函数)

为方便开发者直接使用,以下是补充辅助函数后的完整代码:

// 实现 RFC3986 规范的 URL 编码
function percentEncode(str) {
  return encodeURIComponent(str)
    .replace(/!/g, '%21')
    .replace(/'/g, '%27')
    .replace(/\(/g, '%28')
    .replace(/\)/g, '%29')
    .replace(/\*/g, '%2A');
}

// 计算阿里云 API 签名
function signStringToSign(stringToSign, accessKeySecret) {
  const crypto = require('crypto');
  const key = accessKeySecret + '&';
  const hmac = crypto.createHmac('sha1', key);
  return hmac.update(Buffer.from(stringToSign, 'utf8')).digest('base64');
}

async stToken() {
  let token = await this.ctx.service.memcache.get('aliyuncloud_nls_token');
  if (typeof token === 'undefined') {
    const params = {
      AccessKeyId: this.app.config.alioss.AccessKeyID,
      Action: 'CreateToken',
      Version: '2019-02-28',
      Format: 'JSON',
      RegionId: 'cn-shanghai',
      Timestamp: new Date().toISOString(),
      SignatureMethod: 'HMAC-SHA1',
      SignatureVersion: '1.0',
      SignatureNonce: [
        this.ctx.helper.random(8),
        this.ctx.helper.random(4),
        this.ctx.helper.random(4),
        this.ctx.helper.random(4),
        this.ctx.helper.random(12),
      ].join('-'),
    };

    // 1) 按字母顺序规范化参数
    const sortedKeys = Object.keys(params).sort();
    const queryString = sortedKeys.map(key => `${percentEncode(key)}=${percentEncode(params[key])}`).join('&');

    // 2) 构造待签名字符串
    const stringToSign = `GET&${percentEncode('/')}&${percentEncode(queryString)}`;

    // 3) 计算签名
    const signature = signStringToSign(stringToSign, this.app.config.alioss.AccessKeySecret);

    // 4) 拼接到完整 URL
    const url = `https://nls-meta.cn-shanghai.aliyuncs.com/?Signature=${signature}&${queryString}`;

    const res = await this.ctx.curl(url, { method: 'get', dataType: 'json' }).catch(err => {
      this.ctx.returnError(err.message);
    });

    res.data = res.data || {};
    if ('Token' in res.data) {
      token = res.data.Token;
      // 缓存 Token,提前 1 小时过期
      await this.ctx.service.memcache.set(
        'aliyuncloud_nls_token',
        res.data.Token,
        Number(res.data.Token.ExpireTime + '000') - new Date().getTime() - 3600,
      );
    } else {
      this.ctx.returnError(res.data.ErrMsg || res.data.Message || '获取 token 出错了');
    }
  }

  this.ctx.body = {
    code: 0,
    success: true,
    data: {
      token,
      url: 'wss://nls-gateway.cn-shanghai.aliyuncs.com/ws/v1',
      appkey: 'yFiBOd43tLhrTjH6', // 替换为自身的 AppKey
    },
    message: '',
  };
}

四、注意事项

  1. AccessKey 安全AccessKeyIDAccessKeySecret 需从阿里云 RAM 控制台创建子账号获取,避免使用主账号密钥,且需配置最小权限(仅允许调用 CreateToken 接口);
  2. 缓存可靠性 :若缓存服务异常,需确保代码能降级重新获取 Token,避免服务不可用;
  3. 地域一致性RegionId 、请求域名、WebSocket 地址需保持地域一致(如上海地域均为 cn-shanghai );
  4. 异常监控 :需对 Token 获取失败的情况增加日志记录和告警,便于及时发现问题。

总结

  1. 阿里云语音识别 Token 获取的核心是遵循 OpenAPI 签名规范,完成参数排序、编码、签名三步核心操作;
  2. 缓存 Token 可减少 API 调用次数,提升服务性能,缓存过期时间需提前 1 小时避免使用失效 Token;
  3. 代码实现需注意 AccessKey 安全、异常处理、地域一致性三大要点,确保 Token 获取稳定可靠。

通过本文的解析,开发者可快速掌握阿里云语音识别 Token 的获取逻辑,并基于示例代码适配自身业务场景,完成语音识别服务的对接。


发布评论

发布评论前请先 登录

评论列表 0

暂无评论