基础 Token 鉴权
更新时间: 2024/08/20 15:20:22
网易云信音视频通话和互动直播产品中,鉴权方式分为安全模式和调试模式。如果您在控制台中为指定应用开启了安全模式,则对应应用的用户在加入房间时,需要通过 Token 进行身份校验。
本文档为您介绍鉴权方式和 Token 的生成方式。
鉴权方式
鉴权方式说明
鉴权方式分为安全模式和调试模式,其区别如下:
安全模式 | 调试模式 | |
---|---|---|
鉴权方式 | 默认开启。加入房间时,必须设置正确、可用的 Token,以便网易云信服务器对登录用户进行身份和权限认证。安全性较高。 | 加入房间时,无需设置 Token,即不对登录用户进行鉴权。安全性较低,可能会有盗刷风险。 |
应用场景 | 适用于正式上线的应用。 | 适用于调试、测试阶段的应用。 |
修改鉴权方式
创建应用并开通音视频通话服务后,应用默认的鉴权方式为安全模式。网易云信建议您在应用接入和测试期间使用调试模式,并在正式上线前改为安全模式。
操作步骤如下:
-
登录网易云信控制台。
-
在首页单击指定应用名称。
-
在产品总览区域,单击音视频通话 2.0 产品选项卡中的功能配置。
-
单击基础功能页签,在鉴权方式区域中,单击编辑,选择鉴权方式,并单击保存。
- 在弹出对话框中单击确定。
获取 Token
安全模式下,需要使用 NERTC Token 才能加入房间。其中您可以通过以下两种方式获取 NERTC Token:
方式一 在您的业务服务器根据规则自行计算 NERtc Token(推荐)
基本流程
sequenceDiagram
participant 应用层
participant 业务服务器
participant 云信服务器
participant NERtcSDK
Note over 应用层, NERtcSDK: 申请 Token
应用层 ->> 业务服务器: 请求 Token
业务服务器 -->> 应用层: 按规则计算并返回 Token
Note over 应用层, NERtcSDK: 加入房间时鉴权
rect rgb(191, 223, 255)
应用层 ->> NERtcSDK: 调用客户端 SDK API 加入房间并传入 Token
NERtcSDK ->> 云信服务器: 校验 Token
云信服务器 -->> 应用层: 校验成功并返回成功加入房间的回调
end
流程说明如下:
-
客户端向应用服务器发起安全认证签名的请求。
该步骤交互由您自行完成,具体请参考搭建 Token 服务器。
-
应用服务器根据规则自行计算出 NERtc Token 并返回给客户端。
该步骤由您自行实现,相应的示例代码和实现方式请参考生成 Token和搭建 Token 服务器。
-
客户端通过以上步骤获取 NERTC Token 之后,可以携带 NERTC Token 加入房间。
生成 Token
请参考云信在 GitHub 上提供的示例代码,在您的应用服务器上生成 NERtc Token。示例代码的地址如下:
语言 | 示例代码 | 关键函数 |
---|---|---|
Java | 生成 Token-Java | getToken |
Go | 生成 Token-Go | GetToken |
Node.js | 生成 Token-Nodejs | GetToken |
PHP | 生成 Token-PHP | getToken |
Python | 生成 Token-Python | get_token |
C++ | 生成 Token-C++ | getBasicToken |
C#(dotnet) | 生成 Token-C# | GetToken |
Flutter 端暂不支持高级鉴权功能,引入生成 Token 的示例代码后,您只需要调用 getToken
方法生成 Token 即可,不需要调用 getPermissionKey
方法生成权限密钥。
生成 NERtc Token 的关键参数说明如下表所示。
参数 | 类型 | 描述 |
---|---|---|
channelName | String | RTC 房间名称。channelName 可以为空, 表示该 uid 可以使用这个 token 加入任意房间。 |
uid | Long | 用户在您应用中的 ID,请在您的业务服务器上自行管理并维护。 |
curTime | Long | 当前 Unix 时间戳,单位为毫秒,若传参有误会导致报错 414。 |
ttlSec | Integer | Token 过期时间,单位为秒,最大为 86400 秒(1 天)。 |
appKey | String | 请登录网易云信控制台查看您的应用对应的AppKey 和 AppSecret,具体请参见创建应用并获取 AppKey。 |
appSecret | String |
以 Java 语言为例,生成 Token 的示例代码如下:
public String getToken(String channelName, long uid, int ttlSec) throws Exception {
long curTimeMs = System.currentTimeMillis();
return getTokenWithCurrentTime(channelName, uid, ttlSec, curTimeMs);
}
public String getTokenWithCurrentTime(String channelName, long uid, int ttlSec, long curTimeMs) throws Exception {
if (ttlSec <= 0) {
ttlSec = defaultTTLSec;
}
DynamicToken tokenModel = new DynamicToken();
//生成 signature,将 appkey、uid、curTime、ttl、appsecret 五个字段拼成一个字符串,进行 sha1 编码
tokenModel.signature = sha1(String.format("%s%d%d%d%s%s", appKey, uid, curTimeMs, ttlSec, channelName, appSecret));
tokenModel.curTime = curTimeMs; //获取当前时间戳,单位为毫秒
tokenModel.ttl = ttlSec; //设置Token的过期时间,单位为秒
ObjectMapper objectMapper = new ObjectMapper();
String signature = objectMapper.writeValueAsString(tokenModel);
return Base64.getEncoder().encodeToString(signature.getBytes(StandardCharsets.UTF_8)); // 对JSON字符串进行Base64编码,返回生成的Token字符串
}
private String sha1(String input) throws NoSuchAlgorithmException {
MessageDigest mDigest = MessageDigest.getInstance("SHA-1");
byte[] result = mDigest.digest(input.getBytes(StandardCharsets.UTF_8));
StringBuilder sb = new StringBuilder();
for (byte b : result) {
sb.append(String.format("%02x", b));
}
return sb.toString();
}
public static class DynamicToken {
public String signature;
public long curTime;
public int ttl;
}
}
搭建 Token 服务器
以 Golang 语言为例,您可以参考以下步骤在您的业务服务器搭建 Token Server。
此示例仅供测试使用,不建议用于生产环境。
步骤一 准备运行文件
准备一个 tokenserver.go
文件,使用如下代码:
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
)
const (
appkey = "<Your App Key>" // TODO 替换为您的应用App Key
appsecret = "<Your App Secret>" // TODO 替换为您的应用App Secret
)
var (
// NewTokenServer 实现见 https://github.com/netease-im/G2-API-Examples/blob/main/server/token_server/go/token/token.go
// 说明见 https://github.com/netease-im/G2-API-Examples/tree/main/server/token_server/go
tokenServer, _ = NewTokenServer(appkey, appsecret, 86400)
)
type TokenRequest struct {
ChannelName string `json:"channelName"`
Uid uint64 `json:"uid"`
TTL int `json:"ttl"`
}
func tokenHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
if r.Method != "POST" {
http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
return
}
content, err := ioutil.ReadAll(r.Body)
if err != nil {
http.Error(w, fmt.Sprintf("error to read http body, %v", err), http.StatusBadRequest)
return
}
tokenReq := &TokenRequest{}
if err = json.Unmarshal(content, &tokenReq); err != nil {
http.Error(w, fmt.Sprintf("error to parse http body, %v", err), http.StatusBadRequest)
return
}
token, err := tokenServer.GetToken(tokenReq.ChannelName, tokenReq.Uid, tokenReq.TTL)
if err != nil {
http.Error(w, fmt.Sprintf("error to generate NERtc token, %v", err), http.StatusInternalServerError)
return
}
resp := make(map[string]interface{})
resp["token"] = token
resp["code"] = http.StatusOK
respBody, err := json.Marshal(resp)
if err != nil {
http.Error(w, fmt.Sprintf("error to compose NERtc token response, %v", err), http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
w.Write(respBody)
return
}
func main() {
http.HandleFunc("/nertc-token", tokenHandler)
if err := http.ListenAndServe(":8088", nil); err != nil {
log.Fatal(err)
}
}
步骤二 本地运行服务
在本地运行以下命令。
go run server.go
步骤三 请求并获取 NERtc Token
可以使用如下示例中的 curl 命令请求 Token Server 并获得 NERtc Token。
curl --location --request POST 'localhost:8088/nertc-token' \
--header 'Content-Type: application/json' \
--data-raw '{
"channelName":"channel-881",
"uid":66******5,
"ttl":3600
}'
请求发起成功后,会得到如下响应体。
{
"code": 200,
"token": "eyJjdXJUaW1lIjoxN******lZDRhZTRhOGVjMWIwIiwidHRsIjozNjAwfQ=="
}
方式二 向云信服务器申请 NERTC Token
基本流程
sequenceDiagram
participant 应用层
participant 业务服务器
participant 云信服务器
participant NERtcSDK
Note over 应用层, NERtcSDK: 申请 Token
应用层 ->> 业务服务器: 请求 Token
业务服务器 ->> 云信服务器: 调用 getToken 接口申请 Token
云信服务器 -->> 业务服务器: 生成并返回 Token
业务服务器 -->> 应用层: 返回 Token
Note over 应用层, NERtcSDK: 加入房间时鉴权
应用层 ->> NERtcSDK: 调用客户端 SDK API 加入房间并传入 Token
NERtcSDK -->> 云信服务器: 校验 Token
云信服务器 -->> 应用层: 校验成功并返回成功加入房间的回调
-
客户端向应用服务器发起安全认证签名的请求。
该步骤交互由您自行完成。
-
应用服务器调用接口 getToken,向云信服务器申请 NERTC Token。
请求成功,云信服务器会将 NERTC Token 返回给应用服务器。
-
应用服务器将获取到的 NERTC Token 返回给客户端。
-
客户端通过以上步骤获取 NERTC Token 之后,可以携带 NERTC Token 加入房间。
服务端 getToken 接口说明
- 请求 Header 的相关说明(包括 CurTime、CheckSum)请参考服务端 API 请求结构。
- 请注意此接口的 Content-Type 与其他音视频服务端接口有所差异,为
application/x-www-form-urlencoded;charset=utf-8
。
接口地址信息
POST https://api.netease.im/nimserver/user/getToken.action HTTP/1.1
Content-Type:application/x-www-form-urlencoded;charset=utf-8
请求参数
参数名称 | 类型 | 是否必选 | 示例值 | 描述 |
---|---|---|---|---|
uid | Long | 必选 | 123456 | 用户 ID。 |
channelName | String | 可选 | abc | 绑定的房间名称。未指定 channelName 时,获取的 Token 可以用于加入任何房间。 |
repeatUse | Boolean | 可选 | true | 在 Token 有效期内该用户是否可以多次使用该 Token,默认为 true。
|
expireAt | int | 可选 | 640 | NERTC Token 的过期时间,过期后,该用户将无法通过此 Token 加入房间。 取值范围为 1~86400 秒,默认为 600 秒。 |
如果需要更新 NERTC Token 有效期,可以在该 Token 过期之前,重复使用同样的 uid 和 channelName 再次调用该接口,通过 expireAt 参数修改 Token 有效期。服务端不会返回新的 Token,但会更新此 Token 的有效期。
请求示例
curl请求示例如下:
curlcurl -X POST -H "AppKey: go9dnk********" -H "Nonce: 4tggger********" -H "CurTime: 1443592222" -H "CheckSum: 9e9db3b6c9ab********************" -H "Content-Type: application/x-www-form-urlencoded" -d 'uid=123456' 'https://api.netease.im/nimserver/user/getToken.action'
HttpClient 请求示例如下,此处以 JAVA 为例。
javaimport org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class Test {
public static void main(String[] args) throws Exception{
DefaultHttpClient httpClient = new DefaultHttpClient();
String url = "https://api.netease.im/nimserver/user/getToken.action";
HttpPost httpPost = new HttpPost(url);
String appKey = "94kid09c9ig9k1loimjg012345123456";
String appSecret = "123456789012";
String nonce = "12345";
String curTime = String.valueOf((new Date()).getTime() / 1000L);
String checkSum = CheckSumBuilder.getCheckSum(appSecret, nonce ,curTime);///checkSum的计算方式请参考《服务端API-服务端API说明-调用方式》
// 设置请求的header
httpPost.addHeader("AppKey", appKey);
httpPost.addHeader("Nonce", nonce);
httpPost.addHeader("CurTime", curTime);
httpPost.addHeader("CheckSum", checkSum);
httpPost.addHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");
// 设置请求的参数
List<NameValuePair> nvps = new ArrayList<NameValuePair>();
nvps.add(new BasicNameValuePair("uid", "123456"));
httpPost.setEntity(new UrlEncodedFormEntity(nvps, "utf-8"));
// 执行请求
HttpResponse response = httpClient.execute(httpPost);
// 打印执行结果
System.out.println(EntityUtils.toString(response.getEntity(), "utf-8"));
}
}
返回参数
参数名称 | 类型 | 示例值 | 描述 |
---|---|---|---|
code | Number | 200 | 状态码。200 表示成功调用。 |
token | String | xxxxx | 服务端返回的 Token。 |
返回示例
JSON格式的返回示例如下:
json"Content-Type": "application/json; charset=utf-8"
{
"code":200,
"token":"xxxxx" //安全认证模式下的签名。
}
将 Token 传给客户端用于加入房间
用户携带获取到的 uid
、channelName
和 token
,调用 joinChannel
方法加入房间。
加入房间时,用户 ID 和房间名称需要与申请 Token 时使用的用户 ID 和房间名称一致。
_engine.joinChannel(token,channelName,uid);
常见问题
Token 过期如何处理?
答:Token 过期时,SDK 不会触发任何回调。若用户已加入房间内,不会被踢出房间,也不会影响用户的推拉流动作;再次加入房间时,若仍使用的是过期 Token,SDK 会在触发的 onJoinChannel
回调中返回相关错误码。