音视频通话 2.0
Windows
新手接入指南
产品简介
产品介绍
功能特性
产品优势
应用场景
基本概念
使用限制
产品计费
按量计费
资源包
更新日志
体验 Demo
下载 SDK 和 示例代码
快速开始
接入流程
创建应用
开通服务
快速跑通 Sample Code
集成 SDK
实现音视频通话
Token 鉴权
高级 Token 鉴权
基础功能
设置音频属性
设置视频属性
设置通话音量
屏幕共享
音频共享
通话前网络质量探测
监测发言者音量
通话中质量监测
进阶功能
音频管理
客户端音频录制
原始音频数据
美声变声与混响
耳返
自定义音频采集与渲染
音效与伴音
设置音频订阅优先级
音频裸流传输
媒体补充增强信息
视频管理
视频截图
水印
云信美颜
相芯美颜
自定义视频采集
虚拟背景
视频裸流传输
加入多房间
媒体流管理
媒体流加密
视频流回退
跨房间媒体流转发
云端录制
使用云代理
AI 融合功能
AI 降噪
AI 超分
AI 虚拟背景
最佳实践
房间连接状态管理
实现音视频安全检测
音视频参数配置推荐
API 参考
Windows API 参考
服务端 API
错误码
控制台指南
常见问题处理
FAQ
错题集
获取音频 Dump 文件
音频常见问题排查
视频常见问题排查
服务协议

高级 Token 鉴权

更新时间: 2023/03/10 17:43:46

网易云信音视频通话和互动直播产品中,鉴权方式分为安全模式和调试模式。如果您在控制台中为指定应用开启了安全模式,则对应应用的用户在加入房间时,需要通过 Token 进行身份校验;其中您可以选择在您的 App 中实现基础 Token 鉴权或者高级 Token 鉴权,若您的应用中存在对安全性要求较高的语音或视频通话场景,或者对观众上麦有权限控制的场景,建议您选择高级 Token 鉴权模式,以有效避免客户端遭遇破解攻击的问题。两种鉴权模式的区别如下表。

身份校验项 基础 Token 鉴权 高级 Token 鉴权
检查 App ID
校验用户加入房间的权限
校验用户创建房间的权限 ×
校验用户发送音、视频流的权限 ×

鉴权原理

开启用户权限控制后,当用户加入房间时,云信服务器会在校验 Token 的同时也校验权限密钥(permissionKey,若符合约定的算法规则,则会允许用户加入房间且赋予指定的发流权限。

PermissionKey 中的权限使用了一个 byte 的前六个比特位来表示,其中每个比特位均代表一个权限,权限列表如下:

位数 二进制表示 十进制数字(输入参数) 权限含义
第 1 位 0000 0001 1 仅有发送音频流的权限
第 2 位 0000 0010 2 仅有发送视频流的权限
第 3 位 0000 0100 4 仅有订阅音频流的权限
第 4 位 0000 1000 8 仅有订阅视频流的权限
第 5 位 0001 0000 16 仅有创建房间的权限
第 6 位 0010 0000 32 仅有加入房间的权限

因此可以推算出,表示无权限的十进制参数为 0,表示仅有订阅音、视频流权限的十进制参数为 12,表示既可以发送又可以订阅视频流权限的十进制参数为 15,表示拥有全部权限的十进制参数为 63。

  • Token 由云信服务器或者由您自行计算生成,具体生成逻辑请参考获取 Token
  • 用户权限由您的应用服务器在生成 permissionKey 时确定,所以您需要在您的服务器管理好用户权限列表。

高级 Token 鉴权的流程如下:

uml diagram

实现鉴权流程

步骤一 开通高级 Token 鉴权功能

您需要为指定应用设置用户权限控制,请联系网易云信技术支持,开通高级 Token 鉴权功能。

  • 若您开启了用户权限控制开关,使用该 App Key 的所有用户都必须要在加入房间时传入权限密钥参数,否则无法正常加入房间。
  • 若您关闭了用户权限控制开关,云信服务器默认不会校验权限密钥。

步骤二 在您的服务器生成 permissionKey 并下发给客户端

为了防止客户端遭遇破解攻击的问题,由 permissionKey 定义的权限控制只能由您的服务器计算并返回给您的客户端。

网易云信提供 GO 语言版本的 permissionKey 的计算代码,您可以直接下载并集成至您的服务器,计算代码如下:

package permkey

import (
"bytes"
"compress/zlib"
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"encoding/json"
"strconv"
"strings"
"time"
)

func GenPermKey(uid, expireTime int64, appkey, cname, permSecret string, privilege uint32) (string, error) {
return genPermKey(uid, expireTime, appkey, cname, permSecret, privilege)
}

func genPermKey(uid, expireTime int64, appkey, cname, permSecret string, privilege uint32) (string, error) {
permKeyMap := make(map[string]interface{})
permKeyMap["appkey"] = appkey
permKeyMap["uid"] = uid
permKeyMap["cname"] = cname
permKeyMap["privilege"] = privilege
permKeyMap["expireTime"] = expireTime
curTime := time.Now().Unix()
permKeyMap["curTime"] = curTime

permKeyMap["checksum"] = hmacsha256(appkey, strconv.Itoa(int(uid)), strconv.Itoa(int(curTime)), strconv.Itoa(int(expireTime)), cname, permSecret, strconv.Itoa(int(privilege)))

data, err := json.Marshal(permKeyMap)
if err != nil {
return "", err
}

var b bytes.Buffer
w := zlib.NewWriter(&b)
if _, err = w.Write(data); err != nil {
return "", err
}
if err = w.Close(); err != nil {
return "", err
}

return base64urlEncode(b.Bytes()), nil
}

func hmacsha256(appidStr, uidStr, curTimeStr, expireTimeStr, cname, permSecret, privilegeStr string) string {
var contentToBeSigned string
contentToBeSigned = "appkey:" + appidStr + "\n"
contentToBeSigned += "uid:" + uidStr + "\n"
contentToBeSigned += "curTime:" + curTimeStr + "\n"
contentToBeSigned += "expireTime:" + expireTimeStr + "\n"
contentToBeSigned += "cname:" + cname + "\n"
contentToBeSigned += "privilege:" + privilegeStr + "\n"

h := hmac.New(sha256.New, []byte(permSecret))
h.Write([]byte(contentToBeSigned))
return base64.StdEncoding.EncodeToString(h.Sum(nil))
}

func base64urlEncode(data []byte) string {
str := base64.StdEncoding.EncodeToString(data)
str = strings.Replace(str, "+", "*", -1)
str = strings.Replace(str, "/", "-", -1)
str = strings.Replace(str, "=", "_", -1)
return str
}

PermissionKey 的有效期默认为 24 小时,您可以根据业务调整,区间为 [1s,24h]。

步骤三 将 token 和 permissionKey 传递给 SDK 以对用户进行鉴权

您可以在用户加入房间用户角色变更用户权限密钥需要更新时,将权限控制参数传递给 SDK 以对用户进行鉴权,三种场景下鉴权的具体实现方式如下。

场景一 用户加入房间

在用户调用 joinChannel 方法加入房间时,需要设置 tokenNERtcJoinChannelOptions 中的 permissionKey
适用于加入房间前就明确用户权限的情况。

示例代码如下:

    //加入房间
    nertc::NERtcJoinChannelOptions options;
    options.permission_key = (char *)perm_key.c_str();
    int len = custom_info.size() < kNERtcCustomInfoLength ? custom_info.size() : kNERtcCustomInfoLength - 1;
    memcpy(options.custom_info, custom_info.data(), len);
    int res = nrtc_engine_->joinChannel(token_.c_str(), room_name.c_str(), cur_my_uid_, options);

加入房间时,用户 ID 和房间名称需要与申请 Token 时使用的用户 ID 和房间名称一致。

场景二 用户角色变更

在用户需要连麦时,需要将自己的角色从观众切换到主播,此时需要再次校验用户的发流权限。因此在用户调用 setClientRole 方法切换角色时,需要调用 updatePermissionKey 方法设置新的权限密钥。

示例代码如下:

//更新权限密钥
std::string key = GetPermissonKey();  //向业务服务器重新申请一个 permissionKey
rtc_engine_->updatePermissionKey(key.c_str());

//SDK返回回调
void NRTCEngine::onUpdatePermissionKey(const char *key, NERtcErrorCode error, int timeout) {
    //ToDO
}

场景三 用户权限密钥需要更新

示例代码如下:

    // 收到 onPermissionKeyWillExpire 回调时,向业务服务器重新申请一个 permissionKey,
    // 并调用 updatePermissionKey 将新的 permissionKey 传递给 SDK,建议异步处理 
    void onPermissionKeyWillExpire() /*async*/ {
        std::string key = GetPermissonKey(); //向业务服务器重新申请一个 permissionKey
        return rtc_engine_->updatePermissionKey(key.c_str());
    }

    // onUpdatePermissionKey 返回本人的 updatePermissionKey 结果
    void onUpdatePermissionKey(const char *key, NERtcErrorCode error, int timeout) {
        (void)key;//对应更新的权限key
        (void)error;//更新结果 
            //kNERtcNoError
            //kNERtcErrChannelPermissionKeyError
            //kNERtcErrChannelPermissionKeyTimeout 
        (void)timeout;//超时时间,单位秒,成功时有效 
    }
  • 若在 permissionKey 过期前仍未完成上述操作,则 SDK 会触发 onDisconnect 回调,返回 kNERtcErrChannelPermissionKeyTimeout 错误码,同时客户端会与音视频服务器断开连接。若用户需要再次加入房间,则需要从您的业务服务器获取新的 tokenpermissionKey 并调用 joinChannel 方法,再使用新的 tokenpermissionKey 重新加入房间。

示例代码如下:

    //收到 onDisconnect(kErrorPermissionKeyTimeout)回调时,代表已经权限超时,被服务器提出房间
    void onDisconnect(NERtcErrorCode reason) {
        if (reason == nertc::kNERtcErrChannelPermissionKeyTimeout)
        {
            // 权限超时,已经被踢出房间
        }
    }

相关参考

权限密钥的生命周期图

native权限密钥的生命周期.png

错误码

错误码(ErrorCode 错误原因

kNERtcErrUserPermKeyAuthFailed = 30121

可能原因包括:

  • 应用已开通高级 Token 鉴权,但用户鉴权时没有传入 permissionKey 参数。
  • 应用已开通高级 Token 鉴权,且用户鉴权时传入了 permissionKey 参数,但用户没有对应权限。
  • 应用已开通高级 Token 鉴权,且用户鉴权时传入了 permissionKey 参数,但用户的 permissionKey 已失效。
kNERtcErrChannelPermissionKeyError = 30901 权限密钥错误。
kNERtcErrChannelPermissionKeyTimeout = 30902 权限密钥超时。
kNERtcErrChannelNoPublishPermission = 30911 用户无发流权限。
kNERtcErrChannelNoSubscribePermission = 30912 用户无订阅权限。
此文档是否对你有帮助?
有帮助
我要吐槽
  • 鉴权原理
  • 实现鉴权流程
  • 步骤一 开通高级 Token 鉴权功能
  • 步骤二 在您的服务器生成 permissionKey 并下发给客户端
  • 步骤三 将 token 和 permissionKey 传递给 SDK 以对用户进行鉴权
  • 场景一 用户加入房间
  • 场景二 用户角色变更
  • 场景三 用户权限密钥需要更新
  • 相关参考
  • 权限密钥的生命周期图
  • 错误码