如何使用 NodeJS 为声网Agora 应用构建 Token 服务器

注意:本文于 2021 年 12 月 20 日更新,使用了 Node.js token 服务器 2.0.0 版本。

目前,视频聊天应用的安全性是一个热门话题,随着远程工作和虚拟事件的频率越来越高,人们对安全的需求也随之增加。

在声网Agora 平台内,有一层安保是以 token 认证的形式出现的。token 是用一组给定输入生成的动态密钥,声网Agora 平台使用 token 对用户进行身份验证。

声网Agora 为其 RTC 和 RTM SDK 提供 token 鉴权。本指南将教大家如何使用 Express 和 NodeJS 构建简单的微服务,从而生成 Agora RTC token。因为 RTM 和 RTC 遵循相似的运行模式,所以该示例也适用于 RTM。

前期准备

  • 一个声网Agora 开发者账号(详见 声网注册指南

  • 了解 JavaScript ES6, NodeJSNPM 的基础知识

  • 了解 Express web 服务器的基础知识

新建项目

首先,创建一个新的文件夹,在该文件夹中打开一个终端窗口。然后,在终端中运行 npm init 来设置项目。 接下来,会出现创建项目提示。 我使用的是默认设置,大家可以进行自定义设置。

现在,项目已经创建完成,可以使用以下命令添加 NPM 依赖项( expressagora-access-tokendotenv):

npm install express agora-access-token dotenv

构建 Express 服务器

目前项目已经设置完毕,大家可以在自己喜欢用的代码编辑器中打开该文件夹,查看 package.json,你会注意到入口文件是 index.js,但我们的项目中没有该文件,所以我们必须创建一个新文件并将其命名为 index.js

index.js 文件中,我们会从引用模块开始。 从 Express 中,我们需要 Express 对象。从 agora-access-token 中,我们会用 ES6 的解构赋值来提取对 RtcTokenBuilderRtcRole 对象的引用。我们还将使用包中的 dotenv 作为环境变量:

const express = require('express');
const {RtcTokenBuilder, RtcRole} = require('agora-access-token');
const dotenv = require('dotenv');

我们创建一个 .env 文件并添加声网Agora 资质和将用来监听请求的端口,用来定义我们的变量:

APP_ID=970XXXXX...
APP_CERTIFICATE=5CFXXXXX...
PORT=8080

接下来,定义 app 常量,该常量会把 Express 对象实体化,并支持我们设置自己的服务器:

const app = express();

在为 Express 服务器设置 GET 终端之前,我们需要定义访问端点时调用的函数。第一个函数(nocache)将应用响应头,这将强制浏览器不能缓存响应,确保我们总能获得最新的 token。 大家能注意到,我们在最后调用了 next() 方法,因为该函数是该系列的第一个中间件函数。因此我们需要调用 next(),让 Express 继续执行该系列中下一个函数。

>const nocache = (req, resp, next) => {
  resp.header('Cache-Control', 'private, no-cache, no-store, must-revalidate');
  resp.header('Expires', '-1');
  resp.header('Pragma', 'no-cache');
  next();
};

第二个函数(generateAccessToken)将处理请求并返回 JSON 响应。 现在,我们将定义该函数,并在完成 Express 服务器的设置后添加主体。 这是该系列中的最后一个函数,因此我们不需要 next 参数/函数。

const generateAccessToken = (req, resp) => { };

接下来,我们要定义一个 GET 终端 /rtc,传入 nochachegenerateAccessToken 函数中。

app.get('/access_token', nocache, generateAccessToken);

创建 Express 服务器的最后一步,我们将执行 .listen() 方法,在服务器准备就绪并侦听给定端口后传入 port 并回调。

>app.listen(PORT, () => {
  console.log(`Listening on port: ${PORT}`);
});

生成 Agora RTC Token

现在我们已经完成了 Express 服务器的设置,准备将功能添加到 generateAccessToken 函数中。我们将从设置响应头开始,确保不会遇到任何 CORS 问题。

const generateRTCToken = (req, resp) => {
  resp.header('Access-Control-Allow-Origin', '*');

获取请求参数

接下来,我们将检查 channelName 请求参数,这是必要参数,因此,如果未定义 channelName,我们则需要返回带有 500 响应码的错误和一个带有错误参数的 JSON对象。

...
  const channelName = req.params.channel;
  if (!channelName) {
    return resp.status(500).json({ 'error': 'channel is required' });
  }

对于 uidrole,我们将执行类似的检查:

...
  let uid = req.params.uid;
  if(!uid || uid === '') {
    return resp.status(500).json({ 'error': 'uid is required' });
  }
  // get role
  let role;
  if (req.params.role === 'publisher') {
    role = RtcRole.PUBLISHER;
  } else if (req.params.role === 'audience') {
    role = RtcRole.SUBSCRIBER
  } else {
    return resp.status(500).json({ 'error': 'role is incorrect' });
  }

接下来的几个参数( uid , role , expirationTime)不是必要的,所以我们只需根据需要将其指认为默认值就好了。

对于uid,我们将默认值设置为 0,这使得我们可以生成 “通配符” ,其可以用任何 uid 加入指定频道。 这仅适用于安全性较低的情况下(或在开发过程中),所有用户都可以共享一个 token。

一个低安全性的例子是直播,任何人都可以加入其中并以观众身份观看。

对于频道角色,我们将每个用户默认为 audience,并且仅检查请求是否传达到 broadcaster 那里,在其他方面的值都可以忽略。

请注意:只有默认的情况下,Agora 平台才能执行加入频道的特权,要启用其他特权,您需要通过 Agora 支持 进行开通。

对于有效时间,我们将默认设置为3600秒,这使用户在权限到期之前有一个小时的时间加入频道。 关于截止时间要注意一件事,是token的权限时效必须是一个整数,例如从1970年1月1日开始。我们将使用当前时间,并添加时效。

// get uid
let uid = req.query.uid;
if(!uid uid == ‘’) {
uid = 0;
}
// get role
let role = RtcRole.SUBSCRIBER;
if (req.query.role == ‘publisher’) {
role = RtcRole.PUBLISHER;
}
// get the expire time
let expireTime = req.query.expireTime;
if (!expireTime expireTime == ‘’) {
expireTime = 3600;
} else {
expireTime = parseInt(expireTime, 10);
}
// calculate privilege expire time
const currentTime = Math.floor(Date.now() / 1000);
const privilegeExpireTime = currentTime + expireTime;

构建 Token

现在,我们已经具备了构建 token 的所有条件,现在可以使用 RtcTokenBuilder 对象的 buildTokenWithUid 生成 token 了。

const token = RtcTokenBuilder.buildTokenWithUid(APP_ID, APP_CERTIFICATE, channelName, uid, role, privilegeExpireTime);

返回响应

生成 token 的最后一步是返回包含 token 的 JSON 响应:

...
  return resp.json({ 'rtcToken': token });
}

测试 Token 服务器

让我们回到 package.json 并在 scripts 对象中添加 start 命令。 开始命令将执行 node index.js 命令,以便可以运行服务器实例。

{
  "scripts": {
    "start": "node index.js"
  },
}

启动服务器

我们回到命令提示符窗口并使用新命令:npm start

服务器实例侦听后,我们将在终端窗口中看到“正在侦听端口:8080。

测试端点

现在我们的服务器实例已经处于运行状态,接下来我们要打开网络浏览器进行测试。 对于这些测试,我们将尝试一些省略各种查询参数的少量变体。

我们将从省略所有查询参数开始:

localhost:8080/access_token

这将显示:

{“rtcToken”:“0062ec0d84c41c4442d88ba6f5a2beb828bIAD9qg4N4hd04MvaY6A72m4BjYmO/7+xnRMinaI0ncLzkAx+f9gAAAAAEACS0zcn9gASXwEAAQCGvRBf”}

接下来,我们将通过 “test” 作为 channelName:

localhost:8080/access_token?channelName=test

这将可以输出任何用户都能使用的 token。

{“token”:“0062ec0d84c41c4442d88ba6f5a2beb828bIAD9qg4N4hd04MvaY6A72m4BjYmO/7+xnRMinaI0ncLzkAx+f9gAAAAAEACS0zcn9gASXwEAAQCGvRBf”}

我们可以继续对其余查询参数进行测试,然后会得到与上述情况类似的响应。

localhost:8080/access_token?channelName=test&role=subscriberlocalhost:8080/access_token?channelName=test&role=subscriber&uid=1234localhost:8080/access_token?channelName=test&role=subscriber&uid=1234&expireTime=6400

完成啦!

以上就是所有步骤啦! 如果你没跟上编码过程或想看成品,我已将所有代码上传到了 GitHub。

感谢你抽出宝贵的时间阅读我的教程,如有任何疑问,请在下方评论。 如果你发现有任何改进的空间,欢迎提交你的拉取请求~~

原文作者:Hermes Frangoudis
原文链接:https://www.agora.io/en/blog/how-to-build-a-token-server-for-agora-applications-using-nodejs/

推荐阅读
相关专栏
SDK 教程
164 文章
本专栏仅用于分享音视频相关的技术文章,与其他开发者和声网 研发团队交流、分享行业前沿技术、资讯。发帖前,请参考「社区发帖指南」,方便您更好的展示所发表的文章和内容。