使用 Token 鉴权
鉴权是指在用户访问你的系统前,对用户权限进行校验,声网用 Token 对用户鉴权。Token 也称为动态密钥,是在加入频道时用于校验用户权限的一组字符串。当 App 用户使用声网服务,如加入 RTC 频道时传入 Token,声网服务端会根据 Token 中的信息校验用户的权限。
下图展示声网使用 Token 对用户鉴权的流程:
前提条件
开始前,请确保你的项目满足如下条件:
实现加入频道鉴权
本节介绍如何使用 Token 对加入频道的用户鉴权。为确保你的业务的安全性,声网推荐你对每一个加入频道的客户端都进行鉴权。
生成 Token
声网在 GitHub 上提供一个开源的 Token 生成器代码仓库 AgoraDynamicKey,支持使用 C++、Java、Golang 等多种语言来在你自己的服务器上生成 Token。
以下列出的语言均基于 HMAC-SHA256 算法生成 Token。
以 C++ 为例,你可以通过 privilege_expiration_in_seconds
来统一设置所有权限的过期时间。
示例代码如下:
- C++
- Golang
- Java
- Node.js
- PHP
- Python
- Python3
#include <cstdlib>
#include <iostream>
#include "../src/RtcTokenBuilder2.h"
using namespace agora::tools;
int main(int argc, char const *argv[]) {
(void)argc;
(void)argv;
// 获取环境变量 AGORA_APP_ID 的值。请确保你将该变量设为你在声网控制台获取的 App ID
const char *env_app_id = getenv("AGORA_APP_ID");
std::string app_id = env_app_id ? env_app_id : "";
// 获取环境变量 AGORA_APP_CERTIFICATE 的值。请确保你将该变量设为你在声网控制台获取的 App 证书
const char *env_app_certificate = getenv("AGORA_APP_CERTIFICATE");
std::string app_certificate = env_app_certificate ? env_app_certificate : "";
// 将 channelName 替换为需要加入的频道名
std::string channel_name = "channelName";
// 填入你实际的用户 ID
uint32_t uid = "uid";
// Token 的有效时间,单位秒
uint32_t token_expiration_in_seconds = 3600;
// 所有的权限的有效时间,单位秒,声网建议你将该参数和 Token 的有效时间设为一致
uint32_t privilege_expiration_in_seconds = 3600;
std::string result;
std::cout << "App Id:" << app_id << std::endl;
std::cout << "App Certificate:" << app_certificate << std::endl;
if (app_id == "" || app_certificate == "") {
std::cout << "Need to set environment variable AGORA_APP_ID and "
"AGORA_APP_CERTIFICATE"
<< std::endl;
return -1;
}
// 生成 Token
result = RtcTokenBuilder2::BuildTokenWithUid(
app_id, app_certificate, channel_name, uid, UserRole::kRolePublisher,
token_expiration_in_seconds, privilege_expiration_in_seconds);
std::cout << "Token With Int Uid:" << result << std::endl;
return 0;
}
package main
import (
"fmt"
"os"
rtctokenbuilder "github.com/AgoraIO/Tools/DynamicKey/AgoraDynamicKey/go/src/rtctokenbuilder2"
)
func main() {
// 获取环境变量 AGORA_APP_ID 的值。请确保你将该变量设为你在声网控制台获取的 App ID
appId := os.Getenv("AGORA_APP_ID")
// 获取环境变量 AGORA_APP_CERTIFICATE 的值。请确保你将该变量设为你在声网控制台获取的 App 证书
appCertificate := os.Getenv("AGORA_APP_CERTIFICATE")
// 将 channelName 替换为需要加入的频道名
channelName := "channelName"
// 填入你实际的用户 ID
uid := uint32(uid)
// Token 的有效时间,单位秒
tokenExpirationInSeconds := uint32(3600)
// 所有的权限的有效时间,单位秒,声网建议你将该参数和 Token 的有效时间设为一致
privilegeExpirationInSeconds := uint32(3600)
fmt.Println("App Id:", appId)
fmt.Println("App Certificate:", appCertificate)
if appId == "" || appCertificate == "" {
fmt.Println("Need to set environment variable AGORA_APP_ID and AGORA_APP_CERTIFICATE")
return
}
// 生成 Token
result, err := rtctokenbuilder.BuildTokenWithUid(appId, appCertificate, channelName, uid, rtctokenbuilder.RolePublisher, tokenExpirationInSeconds, privilegeExpirationInSeconds)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("Token with int uid: %s\n", result)
}
}
package io.agora.sample;
import io.agora.media.RtcTokenBuilder2;
import io.agora.media.RtcTokenBuilder2.Role;
public class RtcTokenBuilder2Sample {
// 获取环境变量 AGORA_APP_ID 的值。请确保你将该变量设为你在声网控制台获取的 App ID
static String appId = System.getenv("AGORA_APP_ID");
// 获取环境变量 AGORA_APP_CERTIFICATE 的值。请确保你将该变量设为你在声网控制台获取的 App 证书
static String appCertificate = System.getenv("AGORA_APP_CERTIFICATE");
// 将 channelName 替换为需要加入的频道名
static String channelName = "channelName";
// 填入你实际的用户 ID
static int uid = 2082341273;
// Token 的有效时间,单位秒
static int tokenExpirationInSeconds = 3600;
// 所有的权限的有效时间,单位秒,声网建议你将该参数和 Token 的有效时间设为一致
static int privilegeExpirationInSeconds = 3600;
public static void main(String[] args) {
System.out.printf("App Id: %s\n", appId);
System.out.printf("App Certificate: %s\n", appCertificate);
if (appId == null || appId.isEmpty() || appCertificate == null || appCertificate.isEmpty()) {
System.out.printf("Need to set environment variable AGORA_APP_ID and AGORA_APP_CERTIFICATE\n");
return;
}
// 生成 Token
RtcTokenBuilder2 token = new RtcTokenBuilder2();
String result = token.buildTokenWithUid(appId, appCertificate, channelName, uid, Role.ROLE_PUBLISHER, tokenExpirationInSeconds, privilegeExpirationInSeconds);
System.out.printf("Token with uid: %s\n", result);
}
}
const RtcTokenBuilder = require("../src/RtcTokenBuilder2").RtcTokenBuilder;
const RtcRole = require("../src/RtcTokenBuilder2").Role;
// 获取环境变量 AGORA_APP_ID 的值。请确保你将该变量设为你在声网控制台获取的 App ID
const appId = process.env.AGORA_APP_ID;
// 获取环境变量 AGORA_APP_CERTIFICATE 的值。请确保你将该变量设为你在声网控制台获取的 App 证书
const appCertificate = process.env.AGORA_APP_CERTIFICATE;
// 将 channelName 替换为需要加入的频道名
const channelName = "channelName";
// 填入你实际的用户 ID
const uid = "uid";
// 设为具有发流权限
const role = RtcRole.PUBLISHER;
// Token 的有效时间,单位秒
const tokenExpirationInSecond = 3600;
// 所有的权限的有效时间,单位秒,声网建议你将该参数和 Token 的有效时间设为一致
const privilegeExpirationInSecond = 3600;
console.log("App Id:", appId);
console.log("App Certificate:", appCertificate);
if (appId == undefined || appId == "" || appCertificate == undefined || appCertificate == "") {
console.log("Need to set environment variable AGORA_APP_ID and AGORA_APP_CERTIFICATE");
process.exit(1);
}
// 生成 Token
const tokenWithUid = RtcTokenBuilder.buildTokenWithUid(appId, appCertificate, channelName, uid, role, tokenExpirationInSecond, privilegeExpirationInSecond);
console.log("Token with int uid:", tokenWithUid);
<?php
include("../src/RtcTokenBuilder2.php");
// 获取环境变量 AGORA_APP_ID 的值。请确保你将该变量设为你在声网控制台获取的 App ID
$appId = getenv("AGORA_APP_ID");
// 获取环境变量 AGORA_APP_CERTIFICATE 的值。请确保你将该变量设为你在声网控制台获取的 App 证书
$appCertificate = getenv("AGORA_APP_CERTIFICATE");
// 将 channelName 替换为需要加入的频道名
$channelName = "channelName";
// 填入你实际的用户 ID
$uid = "uid";
// Token 的有效时间,单位秒
$tokenExpirationInSeconds = 3600;
// 所有的权限的有效时间,单位秒,声网建议你将该参数和 Token 的有效时间设为一致
$privilegeExpirationInSeconds = 3600;
echo "App Id: " . $appId . PHP_EOL;
echo "App Certificate: " . $appCertificate . PHP_EOL;
if ($appId == "" || $appCertificate == "") {
echo "Need to set environment variable AGORA_APP_ID and AGORA_APP_CERTIFICATE" . PHP_EOL;
exit;
}
// 生成 Token
$token = RtcTokenBuilder2::buildTokenWithUid($appId, $appCertificate, $channelName, $uid, RtcTokenBuilder2::ROLE_PUBLISHER, $tokenExpirationInSeconds, $privilegeExpirationInSeconds);
echo 'Token with int uid: ' . $token . PHP_EOL;
import os
import sys
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
from src.RtcTokenBuilder2 import *
def main():
# 获取环境变量 AGORA_APP_ID 的值。请确保你将该变量设为你在声网控制台获取的 App ID
app_id = os.environ.get("AGORA_APP_ID")
# 获取环境变量 AGORA_APP_CERTIFICATE 的值。请确保你将该变量设为你在声网控制台获取的 App 证书
app_certificate = os.environ.get("AGORA_APP_CERTIFICATE")
# 将 channel_name 替换为需要加入的频道名
channel_name = "channel_name"
# 填入你实际的用户 ID
uid = "uid"
# Token 的有效时间,单位秒
token_expiration_in_seconds = 3600
# 所有的权限的有效时间,单位秒,声网建议你将该参数和 Token 的有效时间设为一致
privilege_expiration_in_seconds = 3600
print("App Id: %s" % app_id)
print("App Certificate: %s" % app_certificate)
if not app_id or not app_certificate:
print("Need to set environment variable AGORA_APP_ID and AGORA_APP_CERTIFICATE")
return
# 生成 Token
token = RtcTokenBuilder.build_token_with_uid(app_id, app_certificate, channel_name, uid, Role_Publisher,
token_expiration_in_seconds, privilege_expiration_in_seconds)
print("Token with int uid: {}".format(token))
if __name__ == "__main__":
main()
import os
import sys
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
from src.RtcTokenBuilder2 import *
def main():
# 获取环境变量 AGORA_APP_ID 的值。请确保你将该变量设为你在声网控制台获取的 App ID
app_id = os.environ.get("AGORA_APP_ID")
# 获取环境变量 AGORA_APP_CERTIFICATE 的值。请确保你将该变量设为你在声网控制台获取的 App 证书
app_certificate = os.environ.get("AGORA_APP_CERTIFICATE")
# 将 channel_name 替换为需要加入的频道名
channel_name = "channel_name"
# 填入你实际的用户 ID
uid = "uid"
# Token 的有效时间,单位秒
token_expiration_in_seconds = 3600
# 所有的权限的有效时间,单位秒,声网建议你将该参数和 Token 的有效时间设为一致
privilege_expiration_in_seconds = 3600
print("App Id: %s" % app_id)
print("App Certificate: %s" % app_certificate)
if not app_id or not app_certificate:
print("Need to set environment variable AGORA_APP_ID and AGORA_APP_CERTIFICATE")
return
# 生成 Token
token = RtcTokenBuilder.build_token_with_uid(app_id, app_certificate, channel_name, uid, Role_Publisher,
token_expiration_in_seconds, privilege_expiration_in_seconds)
print("Token with int uid: {}".format(token))
if __name__ == "__main__":
main()
- 以上示例代码中使用的是
int
型的用户 ID 来生成 Token。如果你需要使用string
型的用户 ID 来生成 Token,请前往 Token 生成器代码仓库查看对应语言的相关方法及示例代码。 - 如果你需要生成同时具备 RTC 和 RTM 权限的 Token,请参考如何生成具备 RTC 和 RTM 权限的 Token?
使用 Token
本节介绍如何使用 Token 对客户端加入频道的用户进行鉴权。
使用获取到的 Token 和用户 ID、频道名等信息,通过 join
方法加入频道。
加入频道时使用的用户 ID 和频道名必须和生成 Token 时的用户 ID 和频道名一致。
// 频道名
const channel = 'xxx'
// 用户名
const uid = 1
// 请求服务端生成 channel 和 uid 对应的 token
const token = getToken()
// 是否为纯音频场景
const isAudioOnly = false
// 用户 ID 类型 0: int 1: string
const uidType = 0
// 加入频道
await client.join(
token,
channel,
uid,
isAudioOnly,
uidType
);
Token 过期处理
使用 Token 加入频道后,在 Token 过期前 30 秒,SDK 会触发 token-privilege-will-expire
事件;Token 过期时,SDK 会触发 token-privilege-did-expire
事件。
当收到上述回调后,你需要重新在服务端生成新的 Token,然后调用 renewToken
更新 Token。
下列示例代码展示在收到 token-privilege-will-expire
事件提示 Token 即将过期时,调用 renewToken
来更新 Token。
client.on("token-privilege-will-expire", async () => {
// 请求服务端生成 channel 和 uid 对应的新 token
const token = getToken()
// 重新鉴权
await client.renewToken(token)
})
部署与调试
本节介绍如何在你的本地来快速部署一个 Token 生成器,部署成功之后你可以用生成的 Token 调试你的项目。
方式一:通过 Docker 部署 Token 生成器
参考以下步骤在本地部署一个基于 Docker 的 Token 生成器并申请 Token:
在开始前,请确保你已安装 Docker。
-
打开终端,运行下列命令来启动 Token 生成器。将
[YOUR_APP_ID]
和[YOUR_APP_CERTIFICATE]
替换为你在声网控制台获取的 App ID 和 App 证书。Shelldocker run -d -it -p 8080:8080 -e APP_ID=[YOUR_APP_ID] -e APP_CERTIFICATE=[YOUR_APP_CERTIFICATE] --name agora-token-service agoracn/token:0.1.2023053011
-
运行下列命令来向你刚刚部署的 Token 生成器请求一个 Token:
Shellcurl --location 'http://localhost:8080/token/generate' \
--header 'Content-Type: application/json' \
--data '{
"channelName": "channel_name_test",
"uid": "12345678",
"tokenExpireTs": 3600,
"privilegeExpireTs": 3600,
"serviceRtc": {
"enable": true,
"role": 1
}
}'请求成功后,你的终端会返回生成的 Token。
方式二:本地手动部署 Token 生成器
以 Golang 语言为例,你可以参考如下步骤来在你本地搭建并运行一个 Token 生成器来生成 Token:
在开始前,请确保你的 Golang 版本是 1.14 或以上版本。
此示例仅用于演示,请勿用于生产环境中。
-
新建一个
token-server
文件夹,在该文件夹下创建一个server.go
文件,将使用 Golang 生成 Token 的示例代码贴入到该文件中,将其中的appID
和appCertificate
替换为你的 App ID 和 App 证书。 -
打开终端,进入到
token-server
文件路径下,运行如下命令行来为你的 Token 生成器创建go.mod
文件,该文件定义导入路径及依赖项:Go$ go mod init sampleServer
-
运行如下命令行安装依赖。你可以使用 Go 镜像进行加速,例如 https://goproxy.cn。
Go$ go get github.com/AgoraIO/Tools/DynamicKey/AgoraDynamicKey/go/src/rtctokenbuilder2
-
运行如下命令行启动 Token 生成器:
Go$ go run server.go
Token 生成器部署成功后,你的终端会返回根据你填入的 App ID 和 App 证书生成的 Token。
你也可以直接前往声网控制台获取临时 Token 来调试你的项目,详见获取临时 Token。
参考信息
API 参考
以 Golang 语言为例,本节介绍生成 Token 的核心方法。
BuildTokenWithUid
生成 Token 并设置所有的权限过期时间。
func BuildTokenWithUid(appId string, appCertificate string, channelName string, uid uint32, role Role, tokenExpire uint32, privilegeExpire uint32)
参数
appId
:你在声网控制台创建项目时生成的 App ID。appCertificate
:你的项目的 App 证书。channelName
:频道名,长度在 64 个字节以内。uid
:待鉴权用户的用户 ID,32 位无符号整数,范围为 1 到 (2³² - 1), 并保证唯一性。role
:用户权限,决定用户是否能在频道中发流。kRolePublisher
(1):(默认)用户有发流权限,即用户可在频道中发流。kRoleSubscriber
(2):用户有接收权限,即用户可在频道中接收,但不可发流。该参数仅在开启连麦鉴权后才生效。详情参考开启连麦鉴权。
tokenExpire
:Token 的有效时长(秒),最长有效期为 24 小时。privilegeExpire
:所有权限的有效时长(秒)。如设为 0(默认值),表示权限永不过期。
当 Token 过期但权限未过期时,用户仍在频道里并且可以发流,不会触发 SDK 中与 Token 相关的任何回调。但一旦和频道断开连接,用户将无法使用该 Token 加入同一频道。声网建议将 Token 的过期时间和权限过期时间设为一致。